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