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