xref: /openbmc/linux/fs/drop_caches.c (revision 7ebf812b)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * Implement the manual drop-all-pagecache function
4   */
5  
6  #include <linux/pagemap.h>
7  #include <linux/kernel.h>
8  #include <linux/mm.h>
9  #include <linux/fs.h>
10  #include <linux/writeback.h>
11  #include <linux/sysctl.h>
12  #include <linux/gfp.h>
13  #include <linux/swap.h>
14  #include "internal.h"
15  
16  /* A global variable is a bit ugly, but it keeps the code simple */
17  int sysctl_drop_caches;
18  
19  static void drop_pagecache_sb(struct super_block *sb, void *unused)
20  {
21  	struct inode *inode, *toput_inode = NULL;
22  
23  	spin_lock(&sb->s_inode_list_lock);
24  	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
25  		spin_lock(&inode->i_lock);
26  		/*
27  		 * We must skip inodes in unusual state. We may also skip
28  		 * inodes without pages but we deliberately won't in case
29  		 * we need to reschedule to avoid softlockups.
30  		 */
31  		if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
32  		    (mapping_empty(inode->i_mapping) && !need_resched())) {
33  			spin_unlock(&inode->i_lock);
34  			continue;
35  		}
36  		__iget(inode);
37  		spin_unlock(&inode->i_lock);
38  		spin_unlock(&sb->s_inode_list_lock);
39  
40  		invalidate_mapping_pages(inode->i_mapping, 0, -1);
41  		iput(toput_inode);
42  		toput_inode = inode;
43  
44  		cond_resched();
45  		spin_lock(&sb->s_inode_list_lock);
46  	}
47  	spin_unlock(&sb->s_inode_list_lock);
48  	iput(toput_inode);
49  }
50  
51  int drop_caches_sysctl_handler(struct ctl_table *table, int write,
52  		void *buffer, size_t *length, loff_t *ppos)
53  {
54  	int ret;
55  
56  	ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
57  	if (ret)
58  		return ret;
59  	if (write) {
60  		static int stfu;
61  
62  		if (sysctl_drop_caches & 1) {
63  			lru_add_drain_all();
64  			iterate_supers(drop_pagecache_sb, NULL);
65  			count_vm_event(DROP_PAGECACHE);
66  		}
67  		if (sysctl_drop_caches & 2) {
68  			drop_slab();
69  			count_vm_event(DROP_SLAB);
70  		}
71  		if (!stfu) {
72  			pr_info("%s (%d): drop_caches: %d\n",
73  				current->comm, task_pid_nr(current),
74  				sysctl_drop_caches);
75  		}
76  		stfu |= sysctl_drop_caches & 4;
77  	}
78  	return 0;
79  }
80