mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
f2fs: use f2fs_{down, up}_(read, write}_trace() for nat_tree_lock
Under heavy workloads or during background GC/fallocate operations,
nat_tree_lock can experience high lock contention between background
readers (e.g. f2fs_get_node_info() in gc_data_segment) and writers
(e.g. flush_nat_entries, set_node_addr, shrinker).
[375067.327986][T13777] schedule+0x4c/0x114
[375067.327997][T13777] f2fs_get_node_info+0x438/0x5c4
[375067.328002][T13777] f2fs_get_inode_page+0x1e0/0x3f0
[375067.328013][T13777] f2fs_iget+0x88/0x1180
[375067.328024][T13777] f2fs_lookup+0x168/0x3a8
[375067.328035][T13777] path_openat+0xa28/0x1b04
[375067.328046][T13777] do_filp_open+0xac/0x130
[375067.328056][T13777] do_sys_openat2+0x140/0x21c
[375067.328066][T13777] __arm64_sys_openat+0x70/0x9c
[375067.330299][T13777] schedule+0x4c/0x114
[375067.330310][T13777] schedule_preempt_disabled+0x24/0x40
[375067.330321][T13777] rwsem_down_write_slowpath+0x3b4/0x9d0
[375067.330332][T13777] down_write+0x98/0x170
[375067.330343][T13777] set_node_addr+0x74/0x4b4
[375067.330354][T13777] f2fs_new_node_page+0xb0/0x280
[375067.330444][T13777] f2fs_new_inode_page+0x3c/0x64
[375067.330455][T13777] f2fs_init_inode_metadata+0x4c/0x47c
[375067.330461][T13777] f2fs_add_regular_entry+0x258/0x5b8
[375067.330471][T13777] f2fs_add_dentry+0x100/0x158
[375067.330476][T13777] f2fs_do_add_link+0x84/0x140
[375067.330487][T13777] f2fs_create+0xec/0x250
[375067.331759][T13777] schedule+0x4c/0x114
[375067.331770][T13777] f2fs_down_read+0x9c/0xc4
[375067.331781][T13777] f2fs_need_inode_block_update+0x20/0x10c
[375067.331792][T13777] f2fs_do_sync_file+0x478/0x830
[375067.331802][T13777] f2fs_sync_file+0x2c/0x40
This patch converts nat_tree_lock to use the f2fs_{down,up}_{read,write}_trace
infrastructure.
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
parent
9a9ee7408a
commit
46d4246d8d
|
|
@ -986,6 +986,7 @@ Description: This sysfs entry can be used to enable/disable to adjust priority f
|
|||
0x00000008 gc_lock
|
||||
0x00000010 cp_global
|
||||
0x00000020 io_rwsem
|
||||
0x00000040 nat_tree_lock
|
||||
========== ==================
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/lock_duration_priority
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ static bool need_uplift_priority(struct f2fs_rwsem *sem, bool is_write)
|
|||
case LOCK_NAME_GC_LOCK:
|
||||
case LOCK_NAME_CP_GLOBAL:
|
||||
case LOCK_NAME_IO_RWSEM:
|
||||
case LOCK_NAME_NAT_TREE_LOCK:
|
||||
return true;
|
||||
default:
|
||||
f2fs_bug_on(sem->sbi, 1);
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ enum f2fs_lock_name {
|
|||
LOCK_NAME_GC_LOCK,
|
||||
LOCK_NAME_CP_GLOBAL,
|
||||
LOCK_NAME_IO_RWSEM,
|
||||
LOCK_NAME_NAT_TREE_LOCK,
|
||||
LOCK_NAME_MAX,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -401,15 +401,16 @@ bool f2fs_need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid)
|
|||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *e;
|
||||
bool need = false;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
f2fs_down_read(&nm_i->nat_tree_lock);
|
||||
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
e = __lookup_nat_cache(nm_i, nid, false);
|
||||
if (e) {
|
||||
if (!get_nat_flag(e, IS_CHECKPOINTED) &&
|
||||
!get_nat_flag(e, HAS_FSYNCED_INODE))
|
||||
need = true;
|
||||
}
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
return need;
|
||||
}
|
||||
|
||||
|
|
@ -418,12 +419,13 @@ bool f2fs_is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid)
|
|||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *e;
|
||||
bool is_cp = true;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
f2fs_down_read(&nm_i->nat_tree_lock);
|
||||
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
e = __lookup_nat_cache(nm_i, nid, false);
|
||||
if (e && !get_nat_flag(e, IS_CHECKPOINTED))
|
||||
is_cp = false;
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
return is_cp;
|
||||
}
|
||||
|
||||
|
|
@ -432,16 +434,16 @@ bool f2fs_need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino)
|
|||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *e;
|
||||
bool need_update = true;
|
||||
struct f2fs_lock_context lc;
|
||||
struct f2fs_lock_context lc, nlc;
|
||||
|
||||
f2fs_down_read_trace(&sbi->node_write, &lc);
|
||||
f2fs_down_read(&nm_i->nat_tree_lock);
|
||||
f2fs_down_read_trace(&nm_i->nat_tree_lock, &nlc);
|
||||
e = __lookup_nat_cache(nm_i, ino, false);
|
||||
if (e && get_nat_flag(e, HAS_LAST_FSYNC) &&
|
||||
(get_nat_flag(e, IS_CHECKPOINTED) ||
|
||||
get_nat_flag(e, HAS_FSYNCED_INODE)))
|
||||
need_update = false;
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &nlc);
|
||||
f2fs_up_read_trace(&sbi->node_write, &lc);
|
||||
return need_update;
|
||||
}
|
||||
|
|
@ -452,6 +454,7 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
struct nat_entry *new, *e;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
/* Let's mitigate lock contention of nat_tree_lock during checkpoint */
|
||||
if (f2fs_rwsem_is_locked(&sbi->cp_global_sem))
|
||||
|
|
@ -461,7 +464,7 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
if (!new)
|
||||
return;
|
||||
|
||||
f2fs_down_write(&nm_i->nat_tree_lock);
|
||||
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
e = __lookup_nat_cache(nm_i, nid, false);
|
||||
if (!e)
|
||||
e = __init_nat_entry(nm_i, new, ne, false, false);
|
||||
|
|
@ -470,7 +473,7 @@ static void cache_nat_entry(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
nat_get_blkaddr(e) !=
|
||||
le32_to_cpu(ne->block_addr) ||
|
||||
nat_get_version(e) != ne->version);
|
||||
f2fs_up_write(&nm_i->nat_tree_lock);
|
||||
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
if (e != new)
|
||||
__free_nat_entry(new);
|
||||
}
|
||||
|
|
@ -482,8 +485,9 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
|
|||
struct nat_entry *e;
|
||||
struct nat_entry *new = __alloc_nat_entry(sbi, ni->nid, true);
|
||||
bool init_dirty = false;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
f2fs_down_write(&nm_i->nat_tree_lock);
|
||||
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
e = __lookup_nat_cache(nm_i, ni->nid, true);
|
||||
if (!e) {
|
||||
init_dirty = true;
|
||||
|
|
@ -533,15 +537,16 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
|
|||
set_nat_flag(e, HAS_FSYNCED_INODE, true);
|
||||
set_nat_flag(e, HAS_LAST_FSYNC, fsync_done);
|
||||
}
|
||||
f2fs_up_write(&nm_i->nat_tree_lock);
|
||||
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
}
|
||||
|
||||
int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
|
||||
{
|
||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
int nr = nr_shrink;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
if (!f2fs_down_write_trylock(&nm_i->nat_tree_lock))
|
||||
if (!f2fs_down_write_trylock_trace(&nm_i->nat_tree_lock, &lc))
|
||||
return 0;
|
||||
|
||||
spin_lock(&nm_i->nat_list_lock);
|
||||
|
|
@ -563,7 +568,7 @@ int f2fs_try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)
|
|||
}
|
||||
spin_unlock(&nm_i->nat_list_lock);
|
||||
|
||||
f2fs_up_write(&nm_i->nat_tree_lock);
|
||||
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
return nr - nr_shrink;
|
||||
}
|
||||
|
||||
|
|
@ -581,18 +586,19 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
pgoff_t index;
|
||||
int i;
|
||||
bool need_cache = true;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
ni->flag = 0;
|
||||
ni->nid = nid;
|
||||
retry:
|
||||
/* Check nat cache */
|
||||
f2fs_down_read(&nm_i->nat_tree_lock);
|
||||
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
e = __lookup_nat_cache(nm_i, nid, false);
|
||||
if (e) {
|
||||
ni->ino = nat_get_ino(e);
|
||||
ni->blk_addr = nat_get_blkaddr(e);
|
||||
ni->version = nat_get_version(e);
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
if (IS_ENABLED(CONFIG_F2FS_CHECK_FS)) {
|
||||
need_cache = false;
|
||||
goto sanity_check;
|
||||
|
|
@ -610,7 +616,7 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
down_read(&curseg->journal_rwsem);
|
||||
} else if (f2fs_rwsem_is_contended(&nm_i->nat_tree_lock) ||
|
||||
!down_read_trylock(&curseg->journal_rwsem)) {
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
|
@ -621,13 +627,13 @@ int f2fs_get_node_info(struct f2fs_sb_info *sbi, nid_t nid,
|
|||
}
|
||||
up_read(&curseg->journal_rwsem);
|
||||
if (i >= 0) {
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
goto sanity_check;
|
||||
}
|
||||
|
||||
/* Fill node_info from nat page */
|
||||
index = current_nat_addr(sbi, nid);
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
folio = f2fs_get_meta_folio(sbi, index);
|
||||
if (IS_ERR(folio))
|
||||
|
|
@ -2567,8 +2573,9 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi)
|
|||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
unsigned int i, idx;
|
||||
nid_t nid;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
f2fs_down_read(&nm_i->nat_tree_lock);
|
||||
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
for (i = 0; i < nm_i->nat_blocks; i++) {
|
||||
if (!test_bit_le(i, nm_i->nat_block_bitmap))
|
||||
|
|
@ -2591,7 +2598,7 @@ static void scan_free_nid_bits(struct f2fs_sb_info *sbi)
|
|||
out:
|
||||
scan_curseg_cache(sbi);
|
||||
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
}
|
||||
|
||||
static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
|
||||
|
|
@ -2600,6 +2607,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
|
|||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||
int i = 0, ret;
|
||||
nid_t nid = nm_i->next_scan_nid;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
if (unlikely(nid >= nm_i->max_nid))
|
||||
nid = 0;
|
||||
|
|
@ -2626,7 +2634,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
|
|||
f2fs_ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nid), FREE_NID_PAGES,
|
||||
META_NAT, true);
|
||||
|
||||
f2fs_down_read(&nm_i->nat_tree_lock);
|
||||
f2fs_down_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
while (1) {
|
||||
if (!test_bit_le(NAT_BLOCK_OFFSET(nid),
|
||||
|
|
@ -2642,7 +2650,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
|
|||
}
|
||||
|
||||
if (ret) {
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
if (ret == -EFSCORRUPTED) {
|
||||
f2fs_err(sbi, "NAT is corrupt, run fsck to fix it");
|
||||
|
|
@ -2669,7 +2677,7 @@ static int __f2fs_build_free_nids(struct f2fs_sb_info *sbi,
|
|||
/* find free nids from current sum_pages */
|
||||
scan_curseg_cache(sbi);
|
||||
|
||||
f2fs_up_read(&nm_i->nat_tree_lock);
|
||||
f2fs_up_read_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
f2fs_ra_meta_pages(sbi, NAT_BLOCK_OFFSET(nm_i->next_scan_nid),
|
||||
nm_i->ra_nid_pages, META_NAT, false);
|
||||
|
|
@ -3206,21 +3214,22 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
nid_t set_idx = 0;
|
||||
LIST_HEAD(sets);
|
||||
int err = 0;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
/*
|
||||
* during unmount, let's flush nat_bits before checking
|
||||
* nat_cnt[DIRTY_NAT].
|
||||
*/
|
||||
if (enabled_nat_bits(sbi, cpc)) {
|
||||
f2fs_down_write(&nm_i->nat_tree_lock);
|
||||
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
remove_nats_in_journal(sbi);
|
||||
f2fs_up_write(&nm_i->nat_tree_lock);
|
||||
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
}
|
||||
|
||||
if (!nm_i->nat_cnt[DIRTY_NAT])
|
||||
return 0;
|
||||
|
||||
f2fs_down_write(&nm_i->nat_tree_lock);
|
||||
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
/*
|
||||
* if there are no enough space in journal to store dirty nat
|
||||
|
|
@ -3261,7 +3270,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
|||
break;
|
||||
}
|
||||
|
||||
f2fs_up_write(&nm_i->nat_tree_lock);
|
||||
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
/* Allow dirty nats by node block allocation in write_begin */
|
||||
|
||||
return err;
|
||||
|
|
@ -3380,7 +3389,8 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
|
|||
|
||||
mutex_init(&nm_i->build_lock);
|
||||
spin_lock_init(&nm_i->nid_list_lock);
|
||||
init_f2fs_rwsem(&nm_i->nat_tree_lock);
|
||||
init_f2fs_rwsem_trace(&nm_i->nat_tree_lock, sbi,
|
||||
LOCK_NAME_NAT_TREE_LOCK);
|
||||
|
||||
nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid);
|
||||
nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP);
|
||||
|
|
@ -3472,6 +3482,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
|||
struct nat_entry_set **setvec = (struct nat_entry_set **)vec;
|
||||
nid_t nid = 0;
|
||||
unsigned int found;
|
||||
struct f2fs_lock_context lc;
|
||||
|
||||
if (!nm_i)
|
||||
return;
|
||||
|
|
@ -3490,7 +3501,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
|||
spin_unlock(&nm_i->nid_list_lock);
|
||||
|
||||
/* destroy nat cache */
|
||||
f2fs_down_write(&nm_i->nat_tree_lock);
|
||||
f2fs_down_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
while ((found = __gang_lookup_nat_cache(nm_i,
|
||||
nid, NAT_VEC_SIZE, natvec))) {
|
||||
unsigned idx;
|
||||
|
|
@ -3521,7 +3532,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
|||
kmem_cache_free(nat_entry_set_slab, setvec[idx]);
|
||||
}
|
||||
}
|
||||
f2fs_up_write(&nm_i->nat_tree_lock);
|
||||
f2fs_up_write_trace(&nm_i->nat_tree_lock, &lc);
|
||||
|
||||
kvfree(nm_i->nat_block_bitmap);
|
||||
if (nm_i->free_nid_bitmap) {
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ TRACE_DEFINE_ENUM(CP_PHASE_FINISH_CHECKPOINT);
|
|||
{ LOCK_NAME_NODE_WRITE, "node_write" }, \
|
||||
{ LOCK_NAME_GC_LOCK, "gc_lock" }, \
|
||||
{ LOCK_NAME_CP_GLOBAL, "cp_global" }, \
|
||||
{ LOCK_NAME_IO_RWSEM, "io_rwsem" })
|
||||
{ LOCK_NAME_IO_RWSEM, "io_rwsem" }, \
|
||||
{ LOCK_NAME_NAT_TREE_LOCK, "nat_tree_lock" })
|
||||
|
||||
struct f2fs_sb_info;
|
||||
struct f2fs_io_info;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user