18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * f2fs shrinker support
48c2ecf20Sopenharmony_ci *   the basic infra was copied from fs/ubifs/shrinker.c
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2015 Motorola Mobility
78c2ecf20Sopenharmony_ci * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/f2fs_fs.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "f2fs.h"
138c2ecf20Sopenharmony_ci#include "node.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic LIST_HEAD(f2fs_list);
168c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(f2fs_list_lock);
178c2ecf20Sopenharmony_cistatic unsigned int shrinker_run_no;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic unsigned long __count_nat_entries(struct f2fs_sb_info *sbi)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return NM_I(sbi)->nat_cnt[RECLAIMABLE_NAT];
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic unsigned long __count_free_nids(struct f2fs_sb_info *sbi)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	long count = NM_I(sbi)->nid_cnt[FREE_NID] - MAX_FREE_NIDS;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	return count > 0 ? count : 0;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic unsigned long __count_extent_cache(struct f2fs_sb_info *sbi)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	return atomic_read(&sbi->total_zombie_tree) +
348c2ecf20Sopenharmony_ci				atomic_read(&sbi->total_ext_node);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciunsigned long f2fs_shrink_count(struct shrinker *shrink,
388c2ecf20Sopenharmony_ci				struct shrink_control *sc)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct f2fs_sb_info *sbi;
418c2ecf20Sopenharmony_ci	struct list_head *p;
428c2ecf20Sopenharmony_ci	unsigned long count = 0;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	spin_lock(&f2fs_list_lock);
458c2ecf20Sopenharmony_ci	p = f2fs_list.next;
468c2ecf20Sopenharmony_ci	while (p != &f2fs_list) {
478c2ecf20Sopenharmony_ci		sbi = list_entry(p, struct f2fs_sb_info, s_list);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		/* stop f2fs_put_super */
508c2ecf20Sopenharmony_ci		if (!mutex_trylock(&sbi->umount_mutex)) {
518c2ecf20Sopenharmony_ci			p = p->next;
528c2ecf20Sopenharmony_ci			continue;
538c2ecf20Sopenharmony_ci		}
548c2ecf20Sopenharmony_ci		spin_unlock(&f2fs_list_lock);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		/* count extent cache entries */
578c2ecf20Sopenharmony_ci		count += __count_extent_cache(sbi);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		/* count clean nat cache entries */
608c2ecf20Sopenharmony_ci		count += __count_nat_entries(sbi);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		/* count free nids cache entries */
638c2ecf20Sopenharmony_ci		count += __count_free_nids(sbi);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		spin_lock(&f2fs_list_lock);
668c2ecf20Sopenharmony_ci		p = p->next;
678c2ecf20Sopenharmony_ci		mutex_unlock(&sbi->umount_mutex);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
708c2ecf20Sopenharmony_ci	return count;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciunsigned long f2fs_shrink_scan(struct shrinker *shrink,
748c2ecf20Sopenharmony_ci				struct shrink_control *sc)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned long nr = sc->nr_to_scan;
778c2ecf20Sopenharmony_ci	struct f2fs_sb_info *sbi;
788c2ecf20Sopenharmony_ci	struct list_head *p;
798c2ecf20Sopenharmony_ci	unsigned int run_no;
808c2ecf20Sopenharmony_ci	unsigned long freed = 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	spin_lock(&f2fs_list_lock);
838c2ecf20Sopenharmony_ci	do {
848c2ecf20Sopenharmony_ci		run_no = ++shrinker_run_no;
858c2ecf20Sopenharmony_ci	} while (run_no == 0);
868c2ecf20Sopenharmony_ci	p = f2fs_list.next;
878c2ecf20Sopenharmony_ci	while (p != &f2fs_list) {
888c2ecf20Sopenharmony_ci		sbi = list_entry(p, struct f2fs_sb_info, s_list);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		if (sbi->shrinker_run_no == run_no)
918c2ecf20Sopenharmony_ci			break;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		/* stop f2fs_put_super */
948c2ecf20Sopenharmony_ci		if (!mutex_trylock(&sbi->umount_mutex)) {
958c2ecf20Sopenharmony_ci			p = p->next;
968c2ecf20Sopenharmony_ci			continue;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci		spin_unlock(&f2fs_list_lock);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		sbi->shrinker_run_no = run_no;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		/* shrink extent cache entries */
1038c2ecf20Sopenharmony_ci		freed += f2fs_shrink_extent_tree(sbi, nr >> 1);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		/* shrink clean nat cache entries */
1068c2ecf20Sopenharmony_ci		if (freed < nr)
1078c2ecf20Sopenharmony_ci			freed += f2fs_try_to_free_nats(sbi, nr - freed);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		/* shrink free nids cache entries */
1108c2ecf20Sopenharmony_ci		if (freed < nr)
1118c2ecf20Sopenharmony_ci			freed += f2fs_try_to_free_nids(sbi, nr - freed);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		spin_lock(&f2fs_list_lock);
1148c2ecf20Sopenharmony_ci		p = p->next;
1158c2ecf20Sopenharmony_ci		list_move_tail(&sbi->s_list, &f2fs_list);
1168c2ecf20Sopenharmony_ci		mutex_unlock(&sbi->umount_mutex);
1178c2ecf20Sopenharmony_ci		if (freed >= nr)
1188c2ecf20Sopenharmony_ci			break;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
1218c2ecf20Sopenharmony_ci	return freed;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_civoid f2fs_join_shrinker(struct f2fs_sb_info *sbi)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	spin_lock(&f2fs_list_lock);
1278c2ecf20Sopenharmony_ci	list_add_tail(&sbi->s_list, &f2fs_list);
1288c2ecf20Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_civoid f2fs_leave_shrinker(struct f2fs_sb_info *sbi)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	f2fs_shrink_extent_tree(sbi, __count_extent_cache(sbi));
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	spin_lock(&f2fs_list_lock);
1368c2ecf20Sopenharmony_ci	list_del_init(&sbi->s_list);
1378c2ecf20Sopenharmony_ci	spin_unlock(&f2fs_list_lock);
1388c2ecf20Sopenharmony_ci}
139