]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
f2fs: avoid race condition for shrinker count
authorJaegeuk Kim <jaegeuk@kernel.org>
Fri, 6 Nov 2020 21:22:05 +0000 (13:22 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 6 Jan 2021 13:48:40 +0000 (14:48 +0100)
[ Upstream commit a95ba66ac1457b76fe472c8e092ab1006271f16c ]

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>
fs/f2fs/checkpoint.c
fs/f2fs/debug.c
fs/f2fs/f2fs.h
fs/f2fs/node.c
fs/f2fs/node.h
fs/f2fs/shrinker.c

index c966ccc44c15724a331403953d701196d561a590..a57219c51c01a79d53d41264b5a349419455e5e5 100644 (file)
@@ -1596,7 +1596,7 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                        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 &&
                                prefree_segments(sbi) == 0) {
                        f2fs_flush_sit_entries(sbi, cpc);
index 9b0bedd82581b263568695422d7410e679534ace..d8d64447bc9477461c131d7f62f6c2ff38a5d8a7 100644 (file)
@@ -107,8 +107,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
                si->node_pages = NODE_MAPPING(sbi)->nrpages;
        if (sbi->meta_inode)
                si->meta_pages = META_MAPPING(sbi)->nrpages;
-       si->nats = NM_I(sbi)->nat_cnt;
-       si->dirty_nats = NM_I(sbi)->dirty_nat_cnt;
+       si->nats = NM_I(sbi)->nat_cnt[TOTAL_NAT];
+       si->dirty_nats = NM_I(sbi)->nat_cnt[DIRTY_NAT];
        si->sits = MAIN_SEGS(sbi);
        si->dirty_sits = SIT_I(sbi)->dirty_sentries;
        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] +
                                NM_I(sbi)->nid_cnt[PREALLOC_NID]) *
                                sizeof(struct free_nid);
-       si->cache_mem += NM_I(sbi)->nat_cnt * sizeof(struct nat_entry);
-       si->cache_mem += NM_I(sbi)->dirty_nat_cnt *
-                                       sizeof(struct nat_entry_set);
+       si->cache_mem += NM_I(sbi)->nat_cnt[TOTAL_NAT] *
+                               sizeof(struct nat_entry);
+       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);
        for (i = 0; i < MAX_INO_ENTRY; i++)
                si->cache_mem += sbi->im[i].ino_num * sizeof(struct ino_entry);
index 0ddc4a74b9d43048372868fd548ea6a5997307b7..4ca3c2a0a0f5b7b890e07c2b9de9a82053791910 100644 (file)
@@ -797,6 +797,13 @@ enum nid_state {
        MAX_NID_STATE,
 };
 
+enum nat_state {
+       TOTAL_NAT,
+       DIRTY_NAT,
+       RECLAIMABLE_NAT,
+       MAX_NAT_STATE,
+};
+
 struct f2fs_nm_info {
        block_t nat_blkaddr;            /* base disk address of NAT */
        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 list_head nat_entries;   /* cached nat entry list (clean) */
        spinlock_t nat_list_lock;       /* protect clean nat entry list */
-       unsigned int nat_cnt;           /* the # of cached nat entries */
-       unsigned int dirty_nat_cnt;     /* total num of nat entries in set */
+       unsigned int nat_cnt[MAX_NAT_STATE]; /* the # of cached nat entries */
        unsigned int nat_blocks;        /* # of nat blocks */
 
        /* free node ids management */
index 3ac2a4b32375d0e39b7b3db63160d6af04f1c98f..7ce33698ae381893f119cb2bf9fc655abf7b107a 100644 (file)
@@ -62,8 +62,8 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
                                sizeof(struct free_nid)) >> PAGE_SHIFT;
                res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
        } else if (type == NAT_ENTRIES) {
-               mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >>
-                                                       PAGE_SHIFT;
+               mem_size = (nm_i->nat_cnt[TOTAL_NAT] *
+                               sizeof(struct nat_entry)) >> PAGE_SHIFT;
                res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
                if (excess_cached_nats(sbi))
                        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);
        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;
 }
 
@@ -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)
 {
        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);
 }
 
@@ -253,7 +255,8 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,
        if (get_nat_flag(ne, IS_DIRTY))
                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);
 refresh_list:
        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->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,
@@ -2881,14 +2885,17 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        LIST_HEAD(sets);
        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)) {
                down_write(&nm_i->nat_tree_lock);
                remove_nats_in_journal(sbi);
                up_write(&nm_i->nat_tree_lock);
        }
 
-       if (!nm_i->dirty_nat_cnt)
+       if (!nm_i->nat_cnt[DIRTY_NAT])
                return 0;
 
        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.
         */
        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);
 
        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;
        nm_i->nid_cnt[FREE_NID] = 0;
        nm_i->nid_cnt[PREALLOC_NID] = 0;
-       nm_i->nat_cnt = 0;
        nm_i->ram_thresh = DEF_RAM_THRESHOLD;
        nm_i->ra_nid_pages = DEF_RA_NID_PAGES;
        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]);
                }
        }
-       f2fs_bug_on(sbi, nm_i->nat_cnt);
+       f2fs_bug_on(sbi, nm_i->nat_cnt[TOTAL_NAT]);
 
        /* destroy nat set cache */
        nid = 0;
index e05af5df56485eff71c95f58b5d3f877cbcb8e53..4a2e7eaf2b028df5509993754e39e8b4f2f8fa63 100644 (file)
@@ -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)
 {
-       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;
 }
 
 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)
index a467aca29cfefd8a25a16b125356debc54850e33..3ceebaaee38403e61f32da9ddbd93e0536dc63d2 100644 (file)
@@ -18,9 +18,7 @@ static unsigned int shrinker_run_no;
 
 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 count > 0 ? count : 0;
+       return NM_I(sbi)->nat_cnt[RECLAIMABLE_NAT];
 }
 
 static unsigned long __count_free_nids(struct f2fs_sb_info *sbi)