162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Implement the manual drop-all-pagecache function 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/pagemap.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/mm.h> 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/writeback.h> 1162306a36Sopenharmony_ci#include <linux/sysctl.h> 1262306a36Sopenharmony_ci#include <linux/gfp.h> 1362306a36Sopenharmony_ci#include <linux/swap.h> 1462306a36Sopenharmony_ci#include "internal.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* A global variable is a bit ugly, but it keeps the code simple */ 1762306a36Sopenharmony_ciint sysctl_drop_caches; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic void drop_pagecache_sb(struct super_block *sb, void *unused) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct inode *inode, *toput_inode = NULL; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci spin_lock(&sb->s_inode_list_lock); 2462306a36Sopenharmony_ci list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { 2562306a36Sopenharmony_ci spin_lock(&inode->i_lock); 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * We must skip inodes in unusual state. We may also skip 2862306a36Sopenharmony_ci * inodes without pages but we deliberately won't in case 2962306a36Sopenharmony_ci * we need to reschedule to avoid softlockups. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) || 3262306a36Sopenharmony_ci (mapping_empty(inode->i_mapping) && !need_resched())) { 3362306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 3462306a36Sopenharmony_ci continue; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci __iget(inode); 3762306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 3862306a36Sopenharmony_ci spin_unlock(&sb->s_inode_list_lock); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci invalidate_mapping_pages(inode->i_mapping, 0, -1); 4162306a36Sopenharmony_ci iput(toput_inode); 4262306a36Sopenharmony_ci toput_inode = inode; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci cond_resched(); 4562306a36Sopenharmony_ci spin_lock(&sb->s_inode_list_lock); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci spin_unlock(&sb->s_inode_list_lock); 4862306a36Sopenharmony_ci iput(toput_inode); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciint drop_caches_sysctl_handler(struct ctl_table *table, int write, 5262306a36Sopenharmony_ci void *buffer, size_t *length, loff_t *ppos) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci int ret; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci ret = proc_dointvec_minmax(table, write, buffer, length, ppos); 5762306a36Sopenharmony_ci if (ret) 5862306a36Sopenharmony_ci return ret; 5962306a36Sopenharmony_ci if (write) { 6062306a36Sopenharmony_ci static int stfu; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (sysctl_drop_caches & 1) { 6362306a36Sopenharmony_ci lru_add_drain_all(); 6462306a36Sopenharmony_ci iterate_supers(drop_pagecache_sb, NULL); 6562306a36Sopenharmony_ci count_vm_event(DROP_PAGECACHE); 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci if (sysctl_drop_caches & 2) { 6862306a36Sopenharmony_ci drop_slab(); 6962306a36Sopenharmony_ci count_vm_event(DROP_SLAB); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci if (!stfu) { 7262306a36Sopenharmony_ci pr_info("%s (%d): drop_caches: %d\n", 7362306a36Sopenharmony_ci current->comm, task_pid_nr(current), 7462306a36Sopenharmony_ci sysctl_drop_caches); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci stfu |= sysctl_drop_caches & 4; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 80