162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f2fs shrinker support
462306a36Sopenharmony_ci *   the basic infra was copied from fs/ubifs/shrinker.c
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2015 Motorola Mobility
762306a36Sopenharmony_ci * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/fs.h>
1062306a36Sopenharmony_ci#include <linux/f2fs_fs.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "f2fs.h"
1362306a36Sopenharmony_ci#include "node.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic LIST_HEAD(f2fs_list);
1662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(f2fs_list_lock);
1762306a36Sopenharmony_cistatic unsigned int shrinker_run_no;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	return NM_I(sbi)->nat_cnt[RECLAIMABLE_NAT];
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	return count > 0 ? count : 0;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic unsigned long __count_extent_cache(struct f2fs_sb_info *sbi,
3262306a36Sopenharmony_ci					enum extent_type type)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct extent_tree_info *eti = &sbi->extent_tree[type];
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return atomic_read(&eti->total_zombie_tree) +
3762306a36Sopenharmony_ci				atomic_read(&eti->total_ext_node);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ciunsigned long f2fs_shrink_count(struct shrinker *shrink,
4162306a36Sopenharmony_ci				struct shrink_control *sc)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct f2fs_sb_info *sbi;
4462306a36Sopenharmony_ci	struct list_head *p;
4562306a36Sopenharmony_ci	unsigned long count = 0;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	spin_lock(&f2fs_list_lock);
4862306a36Sopenharmony_ci	p = f2fs_list.next;
4962306a36Sopenharmony_ci	while (p != &f2fs_list) {
5062306a36Sopenharmony_ci		sbi = list_entry(p, struct f2fs_sb_info, s_list);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		/* stop f2fs_put_super */
5362306a36Sopenharmony_ci		if (!mutex_trylock(&sbi->umount_mutex)) {
5462306a36Sopenharmony_ci			p = p->next;
5562306a36Sopenharmony_ci			continue;
5662306a36Sopenharmony_ci		}
5762306a36Sopenharmony_ci		spin_unlock(&f2fs_list_lock);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		/* count read extent cache entries */
6062306a36Sopenharmony_ci		count += __count_extent_cache(sbi, EX_READ);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		/* count block age extent cache entries */
6362306a36Sopenharmony_ci		count += __count_extent_cache(sbi, EX_BLOCK_AGE);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		/* count clean nat cache entries */
6662306a36Sopenharmony_ci		count += __count_nat_entries(sbi);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		/* count free nids cache entries */
6962306a36Sopenharmony_ci		count += __count_free_nids(sbi);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		spin_lock(&f2fs_list_lock);
7262306a36Sopenharmony_ci		p = p->next;
7362306a36Sopenharmony_ci		mutex_unlock(&sbi->umount_mutex);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
7662306a36Sopenharmony_ci	return count;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciunsigned long f2fs_shrink_scan(struct shrinker *shrink,
8062306a36Sopenharmony_ci				struct shrink_control *sc)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	unsigned long nr = sc->nr_to_scan;
8362306a36Sopenharmony_ci	struct f2fs_sb_info *sbi;
8462306a36Sopenharmony_ci	struct list_head *p;
8562306a36Sopenharmony_ci	unsigned int run_no;
8662306a36Sopenharmony_ci	unsigned long freed = 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	spin_lock(&f2fs_list_lock);
8962306a36Sopenharmony_ci	do {
9062306a36Sopenharmony_ci		run_no = ++shrinker_run_no;
9162306a36Sopenharmony_ci	} while (run_no == 0);
9262306a36Sopenharmony_ci	p = f2fs_list.next;
9362306a36Sopenharmony_ci	while (p != &f2fs_list) {
9462306a36Sopenharmony_ci		sbi = list_entry(p, struct f2fs_sb_info, s_list);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		if (sbi->shrinker_run_no == run_no)
9762306a36Sopenharmony_ci			break;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		/* stop f2fs_put_super */
10062306a36Sopenharmony_ci		if (!mutex_trylock(&sbi->umount_mutex)) {
10162306a36Sopenharmony_ci			p = p->next;
10262306a36Sopenharmony_ci			continue;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci		spin_unlock(&f2fs_list_lock);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		sbi->shrinker_run_no = run_no;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		/* shrink extent cache entries */
10962306a36Sopenharmony_ci		freed += f2fs_shrink_age_extent_tree(sbi, nr >> 2);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		/* shrink read extent cache entries */
11262306a36Sopenharmony_ci		freed += f2fs_shrink_read_extent_tree(sbi, nr >> 2);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		/* shrink clean nat cache entries */
11562306a36Sopenharmony_ci		if (freed < nr)
11662306a36Sopenharmony_ci			freed += f2fs_try_to_free_nats(sbi, nr - freed);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		/* shrink free nids cache entries */
11962306a36Sopenharmony_ci		if (freed < nr)
12062306a36Sopenharmony_ci			freed += f2fs_try_to_free_nids(sbi, nr - freed);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		spin_lock(&f2fs_list_lock);
12362306a36Sopenharmony_ci		p = p->next;
12462306a36Sopenharmony_ci		list_move_tail(&sbi->s_list, &f2fs_list);
12562306a36Sopenharmony_ci		mutex_unlock(&sbi->umount_mutex);
12662306a36Sopenharmony_ci		if (freed >= nr)
12762306a36Sopenharmony_ci			break;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
13062306a36Sopenharmony_ci	return freed;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_civoid f2fs_join_shrinker(struct f2fs_sb_info *sbi)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	spin_lock(&f2fs_list_lock);
13662306a36Sopenharmony_ci	list_add_tail(&sbi->s_list, &f2fs_list);
13762306a36Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_civoid f2fs_leave_shrinker(struct f2fs_sb_info *sbi)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	f2fs_shrink_read_extent_tree(sbi, __count_extent_cache(sbi, EX_READ));
14362306a36Sopenharmony_ci	f2fs_shrink_age_extent_tree(sbi,
14462306a36Sopenharmony_ci				__count_extent_cache(sbi, EX_BLOCK_AGE));
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	spin_lock(&f2fs_list_lock);
14762306a36Sopenharmony_ci	list_del_init(&sbi->s_list);
14862306a36Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
14962306a36Sopenharmony_ci}
150