f2fs: avoid race condition for shrinker count
[ Upstream commit a95ba66ac1
]
Light reported sometimes shinker gets nat_cnt < dirty_nat_cnt resulting in
wrong do_shinker work. Let's avoid to return insane overflowed value by adding
single tracking value.
Reported-by: Light Hsieh <Light.Hsieh@mediatek.com>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
5.4-rM2-2.2.x-imx-squashed
parent
dbe184f6be
commit
86db71810a
|
@ -1596,7 +1596,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NM_I(sbi)->dirty_nat_cnt == 0 &&
|
if (NM_I(sbi)->nat_cnt[DIRTY_NAT] == 0 &&
|
||||||
SIT_I(sbi)->dirty_sentries == 0 &&
|
SIT_I(sbi)->dirty_sentries == 0 &&
|
||||||
prefree_segments(sbi) == 0) {
|
prefree_segments(sbi) == 0) {
|
||||||
f2fs_flush_sit_entries(sbi, cpc);
|
f2fs_flush_sit_entries(sbi, cpc);
|
||||||
|
|
|
@ -107,8 +107,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||||
si->node_pages = NODE_MAPPING(sbi)->nrpages;
|
si->node_pages = NODE_MAPPING(sbi)->nrpages;
|
||||||
if (sbi->meta_inode)
|
if (sbi->meta_inode)
|
||||||
si->meta_pages = META_MAPPING(sbi)->nrpages;
|
si->meta_pages = META_MAPPING(sbi)->nrpages;
|
||||||
si->nats = NM_I(sbi)->nat_cnt;
|
si->nats = NM_I(sbi)->nat_cnt[TOTAL_NAT];
|
||||||
si->dirty_nats = NM_I(sbi)->dirty_nat_cnt;
|
si->dirty_nats = NM_I(sbi)->nat_cnt[DIRTY_NAT];
|
||||||
si->sits = MAIN_SEGS(sbi);
|
si->sits = MAIN_SEGS(sbi);
|
||||||
si->dirty_sits = SIT_I(sbi)->dirty_sentries;
|
si->dirty_sits = SIT_I(sbi)->dirty_sentries;
|
||||||
si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID];
|
si->free_nids = NM_I(sbi)->nid_cnt[FREE_NID];
|
||||||
|
@ -254,9 +254,10 @@ get_cache:
|
||||||
si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID] +
|
si->cache_mem += (NM_I(sbi)->nid_cnt[FREE_NID] +
|
||||||
NM_I(sbi)->nid_cnt[PREALLOC_NID]) *
|
NM_I(sbi)->nid_cnt[PREALLOC_NID]) *
|
||||||
sizeof(struct free_nid);
|
sizeof(struct free_nid);
|
||||||
si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry);
|
si->cache_mem += NM_I(sbi)->nat_cnt[TOTAL_NAT] *
|
||||||
si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
|
sizeof(struct nat_entry);
|
||||||
sizeof(struct nat_entry_set);
|
si->cache_mem += NM_I(sbi)->nat_cnt[DIRTY_NAT] *
|
||||||
|
sizeof(struct nat_entry_set);
|
||||||
si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
|
si->cache_mem += si->inmem_pages * sizeof(struct inmem_pages);
|
||||||
for (i = 0; i < MAX_INO_ENTRY; i++)
|
for (i = 0; i < MAX_INO_ENTRY; i++)
|
||||||
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
|
si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
|
||||||
|
|
|
@ -797,6 +797,13 @@ enum nid_state {
|
||||||
MAX_NID_STATE,
|
MAX_NID_STATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum nat_state {
|
||||||
|
TOTAL_NAT,
|
||||||
|
DIRTY_NAT,
|
||||||
|
RECLAIMABLE_NAT,
|
||||||
|
MAX_NAT_STATE,
|
||||||
|
};
|
||||||
|
|
||||||
struct f2fs_nm_info {
|
struct f2fs_nm_info {
|
||||||
block_t nat_blkaddr; /* base disk address of NAT */
|
block_t nat_blkaddr; /* base disk address of NAT */
|
||||||
nid_t max_nid; /* maximum possible node ids */
|
nid_t max_nid; /* maximum possible node ids */
|
||||||
|
@ -812,8 +819,7 @@ struct f2fs_nm_info {
|
||||||
struct rw_semaphore nat_tree_lock; /* protect nat_tree_lock */
|
struct rw_semaphore nat_tree_lock; /* protect nat_tree_lock */
|
||||||
struct list_head nat_entries; /* cached nat entry list (clean) */
|
struct list_head nat_entries; /* cached nat entry list (clean) */
|
||||||
spinlock_t nat_list_lock; /* protect clean nat entry list */
|
spinlock_t nat_list_lock; /* protect clean nat entry list */
|
||||||
unsigned int nat_cnt; /* the # of cached nat entries */
|
unsigned int nat_cnt[MAX_NAT_STATE]; /* the # of cached nat entries */
|
||||||
unsigned int dirty_nat_cnt; /* total num of nat entries in set */
|
|
||||||
unsigned int nat_blocks; /* # of nat blocks */
|
unsigned int nat_blocks; /* # of nat blocks */
|
||||||
|
|
||||||
/* free node ids management */
|
/* free node ids management */
|
||||||
|
|
|
@ -62,8 +62,8 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
|
||||||
sizeof(struct free_nid)) >> PAGE_SHIFT;
|
sizeof(struct free_nid)) >> PAGE_SHIFT;
|
||||||
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
|
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
|
||||||
} else if (type == NAT_ENTRIES) {
|
} else if (type == NAT_ENTRIES) {
|
||||||
mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >>
|
mem_size = (nm_i->nat_cnt[TOTAL_NAT] *
|
||||||
PAGE_SHIFT;
|
sizeof(struct nat_entry)) >> PAGE_SHIFT;
|
||||||
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
|
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
|
||||||
if (excess_cached_nats(sbi))
|
if (excess_cached_nats(sbi))
|
||||||
res = false;
|
res = false;
|
||||||
|
@ -177,7 +177,8 @@ static struct nat_entry *__init_nat_entry(struct f2fs_nm_info *nm_i,
|
||||||
list_add_tail(&ne->list, &nm_i->nat_entries);
|
list_add_tail(&ne->list, &nm_i->nat_entries);
|
||||||
spin_unlock(&nm_i->nat_list_lock);
|
spin_unlock(&nm_i->nat_list_lock);
|
||||||
|
|
||||||
nm_i->nat_cnt++;
|
nm_i->nat_cnt[TOTAL_NAT]++;
|
||||||
|
nm_i->nat_cnt[RECLAIMABLE_NAT]++;
|
||||||
return ne;
|
return ne;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +208,8 @@ static unsigned int __gang_lookup_nat_cache(struct f2fs_nm_info *nm_i,
|
||||||
static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e)
|
static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e)
|
||||||
{
|
{
|
||||||
radix_tree_delete(&nm_i->nat_root, nat_get_nid(e));
|
radix_tree_delete(&nm_i->nat_root, nat_get_nid(e));
|
||||||
nm_i->nat_cnt--;
|
nm_i->nat_cnt[TOTAL_NAT]--;
|
||||||
|
nm_i->nat_cnt[RECLAIMABLE_NAT]--;
|
||||||
__free_nat_entry(e);
|
__free_nat_entry(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +255,8 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,
|
||||||
if (get_nat_flag(ne, IS_DIRTY))
|
if (get_nat_flag(ne, IS_DIRTY))
|
||||||
goto refresh_list;
|
goto refresh_list;
|
||||||
|
|
||||||
nm_i->dirty_nat_cnt++;
|
nm_i->nat_cnt[DIRTY_NAT]++;
|
||||||
|
nm_i->nat_cnt[RECLAIMABLE_NAT]--;
|
||||||
set_nat_flag(ne, IS_DIRTY, true);
|
set_nat_flag(ne, IS_DIRTY, true);
|
||||||
refresh_list:
|
refresh_list:
|
||||||
spin_lock(&nm_i->nat_list_lock);
|
spin_lock(&nm_i->nat_list_lock);
|
||||||
|
@ -273,7 +276,8 @@ static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i,
|
||||||
|
|
||||||
set_nat_flag(ne, IS_DIRTY, false);
|
set_nat_flag(ne, IS_DIRTY, false);
|
||||||
set->entry_cnt--;
|
set->entry_cnt--;
|
||||||
nm_i->dirty_nat_cnt--;
|
nm_i->nat_cnt[DIRTY_NAT]--;
|
||||||
|
nm_i->nat_cnt[RECLAIMABLE_NAT]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i,
|
static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i,
|
||||||
|
@ -2881,14 +2885,17 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||||
LIST_HEAD(sets);
|
LIST_HEAD(sets);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/* during unmount, let's flush nat_bits before checking dirty_nat_cnt */
|
/*
|
||||||
|
* during unmount, let's flush nat_bits before checking
|
||||||
|
* nat_cnt[DIRTY_NAT].
|
||||||
|
*/
|
||||||
if (enabled_nat_bits(sbi, cpc)) {
|
if (enabled_nat_bits(sbi, cpc)) {
|
||||||
down_write(&nm_i->nat_tree_lock);
|
down_write(&nm_i->nat_tree_lock);
|
||||||
remove_nats_in_journal(sbi);
|
remove_nats_in_journal(sbi);
|
||||||
up_write(&nm_i->nat_tree_lock);
|
up_write(&nm_i->nat_tree_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nm_i->dirty_nat_cnt)
|
if (!nm_i->nat_cnt[DIRTY_NAT])
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
down_write(&nm_i->nat_tree_lock);
|
down_write(&nm_i->nat_tree_lock);
|
||||||
|
@ -2899,7 +2906,8 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||||
* into nat entry set.
|
* into nat entry set.
|
||||||
*/
|
*/
|
||||||
if (enabled_nat_bits(sbi, cpc) ||
|
if (enabled_nat_bits(sbi, cpc) ||
|
||||||
!__has_cursum_space(journal, nm_i->dirty_nat_cnt, NAT_JOURNAL))
|
!__has_cursum_space(journal,
|
||||||
|
nm_i->nat_cnt[DIRTY_NAT], NAT_JOURNAL))
|
||||||
remove_nats_in_journal(sbi);
|
remove_nats_in_journal(sbi);
|
||||||
|
|
||||||
while ((found = __gang_lookup_nat_set(nm_i,
|
while ((found = __gang_lookup_nat_set(nm_i,
|
||||||
|
@ -3023,7 +3031,6 @@ static int init_node_manager(struct f2fs_sb_info *sbi)
|
||||||
F2FS_RESERVED_NODE_NUM;
|
F2FS_RESERVED_NODE_NUM;
|
||||||
nm_i->nid_cnt[FREE_NID] = 0;
|
nm_i->nid_cnt[FREE_NID] = 0;
|
||||||
nm_i->nid_cnt[PREALLOC_NID] = 0;
|
nm_i->nid_cnt[PREALLOC_NID] = 0;
|
||||||
nm_i->nat_cnt = 0;
|
|
||||||
nm_i->ram_thresh = DEF_RAM_THRESHOLD;
|
nm_i->ram_thresh = DEF_RAM_THRESHOLD;
|
||||||
nm_i->ra_nid_pages = DEF_RA_NID_PAGES;
|
nm_i->ra_nid_pages = DEF_RA_NID_PAGES;
|
||||||
nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD;
|
nm_i->dirty_nats_ratio = DEF_DIRTY_NAT_RATIO_THRESHOLD;
|
||||||
|
@ -3160,7 +3167,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
||||||
__del_from_nat_cache(nm_i, natvec[idx]);
|
__del_from_nat_cache(nm_i, natvec[idx]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f2fs_bug_on(sbi, nm_i->nat_cnt);
|
f2fs_bug_on(sbi, nm_i->nat_cnt[TOTAL_NAT]);
|
||||||
|
|
||||||
/* destroy nat set cache */
|
/* destroy nat set cache */
|
||||||
nid = 0;
|
nid = 0;
|
||||||
|
|
|
@ -123,13 +123,13 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
|
||||||
|
|
||||||
static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi)
|
static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
return NM_I(sbi)->dirty_nat_cnt >= NM_I(sbi)->max_nid *
|
return NM_I(sbi)->nat_cnt[DIRTY_NAT] >= NM_I(sbi)->max_nid *
|
||||||
NM_I(sbi)->dirty_nats_ratio / 100;
|
NM_I(sbi)->dirty_nats_ratio / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool excess_cached_nats(struct f2fs_sb_info *sbi)
|
static inline bool excess_cached_nats(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
return NM_I(sbi)->nat_cnt >= DEF_NAT_CACHE_THRESHOLD;
|
return NM_I(sbi)->nat_cnt[TOTAL_NAT] >= DEF_NAT_CACHE_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool excess_dirty_nodes(struct f2fs_sb_info *sbi)
|
static inline bool excess_dirty_nodes(struct f2fs_sb_info *sbi)
|
||||||
|
|
|
@ -18,9 +18,7 @@ static unsigned int shrinker_run_no;
|
||||||
|
|
||||||
static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
|
static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
long count = NM_I(sbi)->nat_cnt - NM_I(sbi)->dirty_nat_cnt;
|
return NM_I(sbi)->nat_cnt[RECLAIMABLE_NAT];
|
||||||
|
|
||||||
return count > 0 ? count : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
|
static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
|
||||||
|
|
Loading…
Reference in New Issue