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