mirror of https://github.com/OpenIPC/firmware.git
705 lines
20 KiB
Diff
705 lines
20 KiB
Diff
diff -drupN a/fs/f2fs/gc.c b/fs/f2fs/gc.c
|
|
--- a/fs/f2fs/gc.c 2018-08-06 17:23:04.000000000 +0300
|
|
+++ b/fs/f2fs/gc.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -28,17 +28,23 @@ static int gc_thread_func(void *data)
|
|
struct f2fs_sb_info *sbi = data;
|
|
struct f2fs_gc_kthread *gc_th = sbi->gc_thread;
|
|
wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head;
|
|
- long wait_ms;
|
|
+ unsigned int wait_ms;
|
|
|
|
wait_ms = gc_th->min_sleep_time;
|
|
|
|
+ set_freezable();
|
|
do {
|
|
+ wait_event_interruptible_timeout(*wq,
|
|
+ kthread_should_stop() || freezing(current) ||
|
|
+ gc_th->gc_wake,
|
|
+ msecs_to_jiffies(wait_ms));
|
|
+
|
|
+ /* give it a try one time */
|
|
+ if (gc_th->gc_wake)
|
|
+ gc_th->gc_wake = 0;
|
|
+
|
|
if (try_to_freeze())
|
|
continue;
|
|
- else
|
|
- wait_event_interruptible_timeout(*wq,
|
|
- kthread_should_stop(),
|
|
- msecs_to_jiffies(wait_ms));
|
|
if (kthread_should_stop())
|
|
break;
|
|
|
|
@@ -48,10 +54,15 @@ static int gc_thread_func(void *data)
|
|
}
|
|
|
|
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
|
- if (time_to_inject(sbi, FAULT_CHECKPOINT))
|
|
+ if (time_to_inject(sbi, FAULT_CHECKPOINT)) {
|
|
+ f2fs_show_injection_info(FAULT_CHECKPOINT);
|
|
f2fs_stop_checkpoint(sbi, false);
|
|
+ }
|
|
#endif
|
|
|
|
+ if (!sb_start_write_trylock(sbi->sb))
|
|
+ continue;
|
|
+
|
|
/*
|
|
* [GC triggering condition]
|
|
* 0. GC is not conducted currently.
|
|
@@ -65,24 +76,30 @@ static int gc_thread_func(void *data)
|
|
* invalidated soon after by user update or deletion.
|
|
* So, I'd like to wait some time to collect dirty segments.
|
|
*/
|
|
+ if (gc_th->gc_urgent) {
|
|
+ wait_ms = gc_th->urgent_sleep_time;
|
|
+ mutex_lock(&sbi->gc_mutex);
|
|
+ goto do_gc;
|
|
+ }
|
|
+
|
|
if (!mutex_trylock(&sbi->gc_mutex))
|
|
- continue;
|
|
+ goto next;
|
|
|
|
if (!is_idle(sbi)) {
|
|
increase_sleep_time(gc_th, &wait_ms);
|
|
mutex_unlock(&sbi->gc_mutex);
|
|
- continue;
|
|
+ goto next;
|
|
}
|
|
|
|
if (has_enough_invalid_blocks(sbi))
|
|
decrease_sleep_time(gc_th, &wait_ms);
|
|
else
|
|
increase_sleep_time(gc_th, &wait_ms);
|
|
-
|
|
+do_gc:
|
|
stat_inc_bggc_count(sbi);
|
|
|
|
/* if return value is not zero, no victim was selected */
|
|
- if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC)))
|
|
+ if (f2fs_gc(sbi, test_opt(sbi, FORCE_FG_GC), true, NULL_SEGNO))
|
|
wait_ms = gc_th->no_gc_sleep_time;
|
|
|
|
trace_f2fs_background_gc(sbi->sb, wait_ms,
|
|
@@ -90,6 +107,8 @@ static int gc_thread_func(void *data)
|
|
|
|
/* balancing f2fs's metadata periodically */
|
|
f2fs_balance_fs_bg(sbi);
|
|
+next:
|
|
+ sb_end_write(sbi->sb);
|
|
|
|
} while (!kthread_should_stop());
|
|
return 0;
|
|
@@ -107,11 +126,14 @@ int start_gc_thread(struct f2fs_sb_info
|
|
goto out;
|
|
}
|
|
|
|
+ gc_th->urgent_sleep_time = DEF_GC_THREAD_URGENT_SLEEP_TIME;
|
|
gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME;
|
|
gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME;
|
|
gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME;
|
|
|
|
gc_th->gc_idle = 0;
|
|
+ gc_th->gc_urgent = 0;
|
|
+ gc_th->gc_wake= 0;
|
|
|
|
sbi->gc_thread = gc_th;
|
|
init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head);
|
|
@@ -140,12 +162,17 @@ static int select_gc_type(struct f2fs_gc
|
|
{
|
|
int gc_mode = (gc_type == BG_GC) ? GC_CB : GC_GREEDY;
|
|
|
|
- if (gc_th && gc_th->gc_idle) {
|
|
+ if (!gc_th)
|
|
+ return gc_mode;
|
|
+
|
|
+ if (gc_th->gc_idle) {
|
|
if (gc_th->gc_idle == 1)
|
|
gc_mode = GC_CB;
|
|
else if (gc_th->gc_idle == 2)
|
|
gc_mode = GC_GREEDY;
|
|
}
|
|
+ if (gc_th->gc_urgent)
|
|
+ gc_mode = GC_GREEDY;
|
|
return gc_mode;
|
|
}
|
|
|
|
@@ -167,10 +194,17 @@ static void select_policy(struct f2fs_sb
|
|
}
|
|
|
|
/* we need to check every dirty segments in the FG_GC case */
|
|
- if (gc_type != FG_GC && p->max_search > sbi->max_victim_search)
|
|
+ if (gc_type != FG_GC &&
|
|
+ (sbi->gc_thread && !sbi->gc_thread->gc_urgent) &&
|
|
+ p->max_search > sbi->max_victim_search)
|
|
p->max_search = sbi->max_victim_search;
|
|
|
|
- p->offset = sbi->last_victim[p->gc_mode];
|
|
+ /* let's select beginning hot/small space first in no_heap mode*/
|
|
+ if (test_opt(sbi, NOHEAP) &&
|
|
+ (type == CURSEG_HOT_DATA || IS_NODESEG(type)))
|
|
+ p->offset = 0;
|
|
+ else
|
|
+ p->offset = SIT_I(sbi)->last_victim[p->gc_mode];
|
|
}
|
|
|
|
static unsigned int get_max_cost(struct f2fs_sb_info *sbi,
|
|
@@ -180,7 +214,7 @@ static unsigned int get_max_cost(struct
|
|
if (p->alloc_mode == SSR)
|
|
return sbi->blocks_per_seg;
|
|
if (p->gc_mode == GC_GREEDY)
|
|
- return sbi->blocks_per_seg * p->ofs_unit;
|
|
+ return 2 * sbi->blocks_per_seg * p->ofs_unit;
|
|
else if (p->gc_mode == GC_CB)
|
|
return UINT_MAX;
|
|
else /* No other gc_mode */
|
|
@@ -205,7 +239,7 @@ static unsigned int check_bg_victims(str
|
|
continue;
|
|
|
|
clear_bit(secno, dirty_i->victim_secmap);
|
|
- return secno * sbi->segs_per_sec;
|
|
+ return GET_SEG_FROM_SEC(sbi, secno);
|
|
}
|
|
return NULL_SEGNO;
|
|
}
|
|
@@ -213,8 +247,8 @@ static unsigned int check_bg_victims(str
|
|
static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno)
|
|
{
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
- unsigned int secno = GET_SECNO(sbi, segno);
|
|
- unsigned int start = secno * sbi->segs_per_sec;
|
|
+ unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
|
|
+ unsigned int start = GET_SEG_FROM_SEC(sbi, secno);
|
|
unsigned long long mtime = 0;
|
|
unsigned int vblocks;
|
|
unsigned char age = 0;
|
|
@@ -223,7 +257,7 @@ static unsigned int get_cb_cost(struct f
|
|
|
|
for (i = 0; i < sbi->segs_per_sec; i++)
|
|
mtime += get_seg_entry(sbi, start + i)->mtime;
|
|
- vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
|
+ vblocks = get_valid_blocks(sbi, segno, true);
|
|
|
|
mtime = div_u64(mtime, sbi->segs_per_sec);
|
|
vblocks = div_u64(vblocks, sbi->segs_per_sec);
|
|
@@ -250,7 +284,7 @@ static inline unsigned int get_gc_cost(s
|
|
|
|
/* alloc_mode == LFS */
|
|
if (p->gc_mode == GC_GREEDY)
|
|
- return get_valid_blocks(sbi, segno, sbi->segs_per_sec);
|
|
+ return get_valid_blocks(sbi, segno, true);
|
|
else
|
|
return get_cb_cost(sbi, segno);
|
|
}
|
|
@@ -279,6 +313,7 @@ static int get_victim_by_default(struct
|
|
unsigned int *result, int gc_type, int type, char alloc_mode)
|
|
{
|
|
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
|
+ struct sit_info *sm = SIT_I(sbi);
|
|
struct victim_sel_policy p;
|
|
unsigned int secno, last_victim;
|
|
unsigned int last_segment = MAIN_SEGS(sbi);
|
|
@@ -292,10 +327,18 @@ static int get_victim_by_default(struct
|
|
p.min_segno = NULL_SEGNO;
|
|
p.min_cost = get_max_cost(sbi, &p);
|
|
|
|
+ if (*result != NULL_SEGNO) {
|
|
+ if (IS_DATASEG(get_seg_entry(sbi, *result)->type) &&
|
|
+ get_valid_blocks(sbi, *result, false) &&
|
|
+ !sec_usage_check(sbi, GET_SEC_FROM_SEG(sbi, *result)))
|
|
+ p.min_segno = *result;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
if (p.max_search == 0)
|
|
goto out;
|
|
|
|
- last_victim = sbi->last_victim[p.gc_mode];
|
|
+ last_victim = sm->last_victim[p.gc_mode];
|
|
if (p.alloc_mode == LFS && gc_type == FG_GC) {
|
|
p.min_segno = check_bg_victims(sbi);
|
|
if (p.min_segno != NULL_SEGNO)
|
|
@@ -308,9 +351,10 @@ static int get_victim_by_default(struct
|
|
|
|
segno = find_next_bit(p.dirty_segmap, last_segment, p.offset);
|
|
if (segno >= last_segment) {
|
|
- if (sbi->last_victim[p.gc_mode]) {
|
|
- last_segment = sbi->last_victim[p.gc_mode];
|
|
- sbi->last_victim[p.gc_mode] = 0;
|
|
+ if (sm->last_victim[p.gc_mode]) {
|
|
+ last_segment =
|
|
+ sm->last_victim[p.gc_mode];
|
|
+ sm->last_victim[p.gc_mode] = 0;
|
|
p.offset = 0;
|
|
continue;
|
|
}
|
|
@@ -327,7 +371,7 @@ static int get_victim_by_default(struct
|
|
nsearched++;
|
|
}
|
|
|
|
- secno = GET_SECNO(sbi, segno);
|
|
+ secno = GET_SEC_FROM_SEG(sbi, segno);
|
|
|
|
if (sec_usage_check(sbi, secno))
|
|
goto next;
|
|
@@ -345,17 +389,18 @@ static int get_victim_by_default(struct
|
|
}
|
|
next:
|
|
if (nsearched >= p.max_search) {
|
|
- if (!sbi->last_victim[p.gc_mode] && segno <= last_victim)
|
|
- sbi->last_victim[p.gc_mode] = last_victim + 1;
|
|
+ if (!sm->last_victim[p.gc_mode] && segno <= last_victim)
|
|
+ sm->last_victim[p.gc_mode] = last_victim + 1;
|
|
else
|
|
- sbi->last_victim[p.gc_mode] = segno + 1;
|
|
+ sm->last_victim[p.gc_mode] = segno + 1;
|
|
+ sm->last_victim[p.gc_mode] %= MAIN_SEGS(sbi);
|
|
break;
|
|
}
|
|
}
|
|
if (p.min_segno != NULL_SEGNO) {
|
|
got_it:
|
|
if (p.alloc_mode == LFS) {
|
|
- secno = GET_SECNO(sbi, p.min_segno);
|
|
+ secno = GET_SEC_FROM_SEG(sbi, p.min_segno);
|
|
if (gc_type == FG_GC)
|
|
sbi->cur_victim_sec = secno;
|
|
else
|
|
@@ -420,10 +465,10 @@ static int check_valid_map(struct f2fs_s
|
|
struct seg_entry *sentry;
|
|
int ret;
|
|
|
|
- mutex_lock(&sit_i->sentry_lock);
|
|
+ down_read(&sit_i->sentry_lock);
|
|
sentry = get_seg_entry(sbi, segno);
|
|
ret = f2fs_test_bit(offset, sentry->cur_valid_map);
|
|
- mutex_unlock(&sit_i->sentry_lock);
|
|
+ up_read(&sit_i->sentry_lock);
|
|
return ret;
|
|
}
|
|
|
|
@@ -545,7 +590,7 @@ static bool is_alive(struct f2fs_sb_info
|
|
}
|
|
|
|
*nofs = ofs_of_node(node_page);
|
|
- source_blkaddr = datablock_addr(node_page, ofs_in_node);
|
|
+ source_blkaddr = datablock_addr(NULL, node_page, ofs_in_node);
|
|
f2fs_put_page(node_page, 1);
|
|
|
|
if (source_blkaddr != blkaddr)
|
|
@@ -553,14 +598,22 @@ static bool is_alive(struct f2fs_sb_info
|
|
return true;
|
|
}
|
|
|
|
-static void move_encrypted_block(struct inode *inode, block_t bidx)
|
|
+/*
|
|
+ * Move data block via META_MAPPING while keeping locked data page.
|
|
+ * This can be used to move blocks, aka LBAs, directly on disk.
|
|
+ */
|
|
+static void move_data_block(struct inode *inode, block_t bidx,
|
|
+ unsigned int segno, int off)
|
|
{
|
|
struct f2fs_io_info fio = {
|
|
.sbi = F2FS_I_SB(inode),
|
|
+ .ino = inode->i_ino,
|
|
.type = DATA,
|
|
+ .temp = COLD,
|
|
.op = REQ_OP_READ,
|
|
- .op_flags = READ_SYNC,
|
|
+ .op_flags = 0,
|
|
.encrypted_page = NULL,
|
|
+ .in_list = false,
|
|
};
|
|
struct dnode_of_data dn;
|
|
struct f2fs_summary sum;
|
|
@@ -574,6 +627,17 @@ static void move_encrypted_block(struct
|
|
if (!page)
|
|
return;
|
|
|
|
+ if (!check_valid_map(F2FS_I_SB(inode), segno, off))
|
|
+ goto out;
|
|
+
|
|
+ if (f2fs_is_atomic_file(inode))
|
|
+ goto out;
|
|
+
|
|
+ if (f2fs_is_pinned_file(inode)) {
|
|
+ f2fs_pin_file_control(inode, true);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
set_new_dnode(&dn, inode, NULL, NULL, 0);
|
|
err = get_dnode_of_data(&dn, bidx, LOOKUP_NODE);
|
|
if (err)
|
|
@@ -598,10 +662,10 @@ static void move_encrypted_block(struct
|
|
fio.new_blkaddr = fio.old_blkaddr = dn.data_blkaddr;
|
|
|
|
allocate_data_block(fio.sbi, NULL, fio.old_blkaddr, &newaddr,
|
|
- &sum, CURSEG_COLD_DATA);
|
|
+ &sum, CURSEG_COLD_DATA, NULL, false);
|
|
|
|
- fio.encrypted_page = pagecache_get_page(META_MAPPING(fio.sbi), newaddr,
|
|
- FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
|
+ fio.encrypted_page = f2fs_pagecache_get_page(META_MAPPING(fio.sbi),
|
|
+ newaddr, FGP_LOCK | FGP_CREAT, GFP_NOFS);
|
|
if (!fio.encrypted_page) {
|
|
err = -ENOMEM;
|
|
goto recover_block;
|
|
@@ -629,14 +693,22 @@ static void move_encrypted_block(struct
|
|
dec_page_count(fio.sbi, F2FS_DIRTY_META);
|
|
|
|
set_page_writeback(fio.encrypted_page);
|
|
+ ClearPageError(page);
|
|
|
|
/* allocate block address */
|
|
f2fs_wait_on_page_writeback(dn.node_page, NODE, true);
|
|
|
|
fio.op = REQ_OP_WRITE;
|
|
- fio.op_flags = WRITE_SYNC;
|
|
+ fio.op_flags = REQ_SYNC;
|
|
fio.new_blkaddr = newaddr;
|
|
- f2fs_submit_page_mbio(&fio);
|
|
+ err = f2fs_submit_page_write(&fio);
|
|
+ if (err) {
|
|
+ if (PageWriteback(fio.encrypted_page))
|
|
+ end_page_writeback(fio.encrypted_page);
|
|
+ goto put_page_out;
|
|
+ }
|
|
+
|
|
+ f2fs_update_iostat(fio.sbi, FS_GC_DATA_IO, F2FS_BLKSIZE);
|
|
|
|
f2fs_update_data_blkaddr(&dn, newaddr);
|
|
set_inode_flag(inode, FI_APPEND_WRITE);
|
|
@@ -654,7 +726,8 @@ out:
|
|
f2fs_put_page(page, 1);
|
|
}
|
|
|
|
-static void move_data_page(struct inode *inode, block_t bidx, int gc_type)
|
|
+static void move_data_page(struct inode *inode, block_t bidx, int gc_type,
|
|
+ unsigned int segno, int off)
|
|
{
|
|
struct page *page;
|
|
|
|
@@ -662,6 +735,17 @@ static void move_data_page(struct inode
|
|
if (IS_ERR(page))
|
|
return;
|
|
|
|
+ if (!check_valid_map(F2FS_I_SB(inode), segno, off))
|
|
+ goto out;
|
|
+
|
|
+ if (f2fs_is_atomic_file(inode))
|
|
+ goto out;
|
|
+ if (f2fs_is_pinned_file(inode)) {
|
|
+ if (gc_type == FG_GC)
|
|
+ f2fs_pin_file_control(inode, true);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
if (gc_type == BG_GC) {
|
|
if (PageWriteback(page))
|
|
goto out;
|
|
@@ -670,11 +754,16 @@ static void move_data_page(struct inode
|
|
} else {
|
|
struct f2fs_io_info fio = {
|
|
.sbi = F2FS_I_SB(inode),
|
|
+ .ino = inode->i_ino,
|
|
.type = DATA,
|
|
+ .temp = COLD,
|
|
.op = REQ_OP_WRITE,
|
|
- .op_flags = WRITE_SYNC,
|
|
+ .op_flags = REQ_SYNC,
|
|
+ .old_blkaddr = NULL_ADDR,
|
|
.page = page,
|
|
.encrypted_page = NULL,
|
|
+ .need_lock = LOCK_REQ,
|
|
+ .io_type = FS_GC_DATA_IO,
|
|
};
|
|
bool is_dirty = PageDirty(page);
|
|
int err;
|
|
@@ -682,8 +771,10 @@ static void move_data_page(struct inode
|
|
retry:
|
|
set_page_dirty(page);
|
|
f2fs_wait_on_page_writeback(page, DATA, true);
|
|
- if (clear_page_dirty_for_io(page))
|
|
+ if (clear_page_dirty_for_io(page)) {
|
|
inode_dec_dirty_pages(inode);
|
|
+ remove_dirty_inode(inode);
|
|
+ }
|
|
|
|
set_cold_data(page);
|
|
|
|
@@ -697,8 +788,6 @@ retry:
|
|
if (is_dirty)
|
|
set_page_dirty(page);
|
|
}
|
|
-
|
|
- clear_cold_data(page);
|
|
}
|
|
out:
|
|
f2fs_put_page(page, 1);
|
|
@@ -767,17 +856,23 @@ next_step:
|
|
if (IS_ERR(inode) || is_bad_inode(inode))
|
|
continue;
|
|
|
|
- /* if encrypted inode, let's go phase 3 */
|
|
- if (f2fs_encrypted_inode(inode) &&
|
|
- S_ISREG(inode->i_mode)) {
|
|
+ /* if inode uses special I/O path, let's go phase 3 */
|
|
+ if (f2fs_post_read_required(inode)) {
|
|
add_gc_inode(gc_list, inode);
|
|
continue;
|
|
}
|
|
|
|
+ if (!down_write_trylock(
|
|
+ &F2FS_I(inode)->dio_rwsem[WRITE])) {
|
|
+ iput(inode);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
start_bidx = start_bidx_of_node(nofs, inode);
|
|
data_page = get_read_data_page(inode,
|
|
start_bidx + ofs_in_node, REQ_RAHEAD,
|
|
true);
|
|
+ up_write(&F2FS_I(inode)->dio_rwsem[WRITE]);
|
|
if (IS_ERR(data_page)) {
|
|
iput(inode);
|
|
continue;
|
|
@@ -803,14 +898,18 @@ next_step:
|
|
continue;
|
|
}
|
|
locked = true;
|
|
+
|
|
+ /* wait for all inflight aio data */
|
|
+ inode_dio_wait(inode);
|
|
}
|
|
|
|
start_bidx = start_bidx_of_node(nofs, inode)
|
|
+ ofs_in_node;
|
|
- if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode))
|
|
- move_encrypted_block(inode, start_bidx);
|
|
+ if (f2fs_post_read_required(inode))
|
|
+ move_data_block(inode, start_bidx, segno, off);
|
|
else
|
|
- move_data_page(inode, start_bidx, gc_type);
|
|
+ move_data_page(inode, start_bidx, gc_type,
|
|
+ segno, off);
|
|
|
|
if (locked) {
|
|
up_write(&fi->dio_rwsem[WRITE]);
|
|
@@ -831,10 +930,10 @@ static int __get_victim(struct f2fs_sb_i
|
|
struct sit_info *sit_i = SIT_I(sbi);
|
|
int ret;
|
|
|
|
- mutex_lock(&sit_i->sentry_lock);
|
|
+ down_write(&sit_i->sentry_lock);
|
|
ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type,
|
|
NO_CHECK_TYPE, LFS);
|
|
- mutex_unlock(&sit_i->sentry_lock);
|
|
+ up_write(&sit_i->sentry_lock);
|
|
return ret;
|
|
}
|
|
|
|
@@ -847,7 +946,7 @@ static int do_garbage_collect(struct f2f
|
|
struct blk_plug plug;
|
|
unsigned int segno = start_segno;
|
|
unsigned int end_segno = start_segno + sbi->segs_per_sec;
|
|
- int sec_freed = 0;
|
|
+ int seg_freed = 0;
|
|
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
|
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
|
|
|
@@ -871,7 +970,7 @@ static int do_garbage_collect(struct f2f
|
|
GET_SUM_BLOCK(sbi, segno));
|
|
f2fs_put_page(sum_page, 0);
|
|
|
|
- if (get_valid_blocks(sbi, segno, 1) == 0 ||
|
|
+ if (get_valid_blocks(sbi, segno, false) == 0 ||
|
|
!PageUptodate(sum_page) ||
|
|
unlikely(f2fs_cp_error(sbi)))
|
|
goto next;
|
|
@@ -882,11 +981,10 @@ static int do_garbage_collect(struct f2f
|
|
/*
|
|
* this is to avoid deadlock:
|
|
* - lock_page(sum_page) - f2fs_replace_block
|
|
- * - check_valid_map() - mutex_lock(sentry_lock)
|
|
- * - mutex_lock(sentry_lock) - change_curseg()
|
|
+ * - check_valid_map() - down_write(sentry_lock)
|
|
+ * - down_read(sentry_lock) - change_curseg()
|
|
* - lock_page(sum_page)
|
|
*/
|
|
-
|
|
if (type == SUM_TYPE_NODE)
|
|
gc_node_segment(sbi, sum->entries, segno, gc_type);
|
|
else
|
|
@@ -894,87 +992,113 @@ static int do_garbage_collect(struct f2f
|
|
gc_type);
|
|
|
|
stat_inc_seg_count(sbi, type, gc_type);
|
|
+
|
|
+ if (gc_type == FG_GC &&
|
|
+ get_valid_blocks(sbi, segno, false) == 0)
|
|
+ seg_freed++;
|
|
next:
|
|
f2fs_put_page(sum_page, 0);
|
|
}
|
|
|
|
if (gc_type == FG_GC)
|
|
- f2fs_submit_merged_bio(sbi,
|
|
- (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE);
|
|
+ f2fs_submit_merged_write(sbi,
|
|
+ (type == SUM_TYPE_NODE) ? NODE : DATA);
|
|
|
|
blk_finish_plug(&plug);
|
|
|
|
- if (gc_type == FG_GC &&
|
|
- get_valid_blocks(sbi, start_segno, sbi->segs_per_sec) == 0)
|
|
- sec_freed = 1;
|
|
-
|
|
stat_inc_call_count(sbi->stat_info);
|
|
|
|
- return sec_freed;
|
|
+ return seg_freed;
|
|
}
|
|
|
|
-int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
|
|
+int f2fs_gc(struct f2fs_sb_info *sbi, bool sync,
|
|
+ bool background, unsigned int segno)
|
|
{
|
|
- unsigned int segno;
|
|
int gc_type = sync ? FG_GC : BG_GC;
|
|
- int sec_freed = 0;
|
|
- int ret = -EINVAL;
|
|
+ int sec_freed = 0, seg_freed = 0, total_freed = 0;
|
|
+ int ret = 0;
|
|
struct cp_control cpc;
|
|
+ unsigned int init_segno = segno;
|
|
struct gc_inode_list gc_list = {
|
|
.ilist = LIST_HEAD_INIT(gc_list.ilist),
|
|
.iroot = RADIX_TREE_INIT(GFP_NOFS),
|
|
};
|
|
|
|
+ trace_f2fs_gc_begin(sbi->sb, sync, background,
|
|
+ get_pages(sbi, F2FS_DIRTY_NODES),
|
|
+ get_pages(sbi, F2FS_DIRTY_DENTS),
|
|
+ get_pages(sbi, F2FS_DIRTY_IMETA),
|
|
+ free_sections(sbi),
|
|
+ free_segments(sbi),
|
|
+ reserved_segments(sbi),
|
|
+ prefree_segments(sbi));
|
|
+
|
|
cpc.reason = __get_cp_reason(sbi);
|
|
gc_more:
|
|
- segno = NULL_SEGNO;
|
|
-
|
|
- if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
|
|
+ if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE))) {
|
|
+ ret = -EINVAL;
|
|
goto stop;
|
|
+ }
|
|
if (unlikely(f2fs_cp_error(sbi))) {
|
|
ret = -EIO;
|
|
goto stop;
|
|
}
|
|
|
|
- if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed, 0)) {
|
|
- gc_type = FG_GC;
|
|
+ if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0, 0)) {
|
|
/*
|
|
- * If there is no victim and no prefree segment but still not
|
|
- * enough free sections, we should flush dent/node blocks and do
|
|
- * garbage collections.
|
|
+ * For example, if there are many prefree_segments below given
|
|
+ * threshold, we can make them free by checkpoint. Then, we
|
|
+ * secure free segments which doesn't need fggc any more.
|
|
*/
|
|
- if (__get_victim(sbi, &segno, gc_type) ||
|
|
- prefree_segments(sbi)) {
|
|
- ret = write_checkpoint(sbi, &cpc);
|
|
- if (ret)
|
|
- goto stop;
|
|
- segno = NULL_SEGNO;
|
|
- } else if (has_not_enough_free_secs(sbi, 0, 0)) {
|
|
+ if (prefree_segments(sbi)) {
|
|
ret = write_checkpoint(sbi, &cpc);
|
|
if (ret)
|
|
goto stop;
|
|
}
|
|
+ if (has_not_enough_free_secs(sbi, 0, 0))
|
|
+ gc_type = FG_GC;
|
|
}
|
|
|
|
- if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
|
|
+ /* f2fs_balance_fs doesn't need to do BG_GC in critical path. */
|
|
+ if (gc_type == BG_GC && !background) {
|
|
+ ret = -EINVAL;
|
|
goto stop;
|
|
- ret = 0;
|
|
+ }
|
|
+ if (!__get_victim(sbi, &segno, gc_type)) {
|
|
+ ret = -ENODATA;
|
|
+ goto stop;
|
|
+ }
|
|
|
|
- if (do_garbage_collect(sbi, segno, &gc_list, gc_type) &&
|
|
- gc_type == FG_GC)
|
|
+ seg_freed = do_garbage_collect(sbi, segno, &gc_list, gc_type);
|
|
+ if (gc_type == FG_GC && seg_freed == sbi->segs_per_sec)
|
|
sec_freed++;
|
|
+ total_freed += seg_freed;
|
|
|
|
if (gc_type == FG_GC)
|
|
sbi->cur_victim_sec = NULL_SEGNO;
|
|
|
|
if (!sync) {
|
|
- if (has_not_enough_free_secs(sbi, sec_freed, 0))
|
|
+ if (has_not_enough_free_secs(sbi, sec_freed, 0)) {
|
|
+ segno = NULL_SEGNO;
|
|
goto gc_more;
|
|
+ }
|
|
|
|
if (gc_type == FG_GC)
|
|
ret = write_checkpoint(sbi, &cpc);
|
|
}
|
|
stop:
|
|
+ SIT_I(sbi)->last_victim[ALLOC_NEXT] = 0;
|
|
+ SIT_I(sbi)->last_victim[FLUSH_DEVICE] = init_segno;
|
|
+
|
|
+ trace_f2fs_gc_end(sbi->sb, ret, total_freed, sec_freed,
|
|
+ get_pages(sbi, F2FS_DIRTY_NODES),
|
|
+ get_pages(sbi, F2FS_DIRTY_DENTS),
|
|
+ get_pages(sbi, F2FS_DIRTY_IMETA),
|
|
+ free_sections(sbi),
|
|
+ free_segments(sbi),
|
|
+ reserved_segments(sbi),
|
|
+ prefree_segments(sbi));
|
|
+
|
|
mutex_unlock(&sbi->gc_mutex);
|
|
|
|
put_gc_inode(&gc_list);
|
|
@@ -986,7 +1110,7 @@ stop:
|
|
|
|
void build_gc_manager(struct f2fs_sb_info *sbi)
|
|
{
|
|
- u64 main_count, resv_count, ovp_count, blocks_per_sec;
|
|
+ u64 main_count, resv_count, ovp_count;
|
|
|
|
DIRTY_I(sbi)->v_ops = &default_v_ops;
|
|
|
|
@@ -994,8 +1118,13 @@ void build_gc_manager(struct f2fs_sb_inf
|
|
main_count = SM_I(sbi)->main_segments << sbi->log_blocks_per_seg;
|
|
resv_count = SM_I(sbi)->reserved_segments << sbi->log_blocks_per_seg;
|
|
ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg;
|
|
- blocks_per_sec = sbi->blocks_per_seg * sbi->segs_per_sec;
|
|
|
|
- sbi->fggc_threshold = div_u64((main_count - ovp_count) * blocks_per_sec,
|
|
- (main_count - resv_count));
|
|
+ sbi->fggc_threshold = div64_u64((main_count - ovp_count) *
|
|
+ BLKS_PER_SEC(sbi), (main_count - resv_count));
|
|
+ sbi->gc_pin_file_threshold = DEF_GC_FAILED_PINNED_FILES;
|
|
+
|
|
+ /* give warm/cold data area from slower device */
|
|
+ if (sbi->s_ndevs && sbi->segs_per_sec == 1)
|
|
+ SIT_I(sbi)->last_victim[ALLOC_NEXT] =
|
|
+ GET_SEGNO(sbi, FDEV(0).end_blk) + 1;
|
|
}
|