-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathselective_drop_caches.c
More file actions
134 lines (107 loc) · 2.88 KB
/
selective_drop_caches.c
File metadata and controls
134 lines (107 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
* Implement the manual selective drop pagecache function
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/writeback.h>
#include <linux/sysctl.h>
#include <linux/gfp.h>
#include <linux/limits.h>
#include <linux/namei.h>
/* Defines the license for this LKM */
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Selective pagecache drop module");
MODULE_AUTHOR("Maksym Planeta");
static void clean_mapping(struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
if (!inode)
return;
printk(KERN_INFO "Clean %s with %d pages\n", dentry->d_name.name,
inode->i_mapping ? inode->i_mapping->nrpages : -1);
if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
(inode->i_mapping->nrpages == 0)) {
return;
}
invalidate_mapping_pages(inode->i_mapping, 0, -1);
}
static void clean_all_dentries_locked(struct dentry *dentry)
{
struct dentry *child;
list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
clean_all_dentries_locked(child);
}
clean_mapping(dentry);
}
static void clean_all_dentries(struct dentry *dentry)
{
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
clean_all_dentries_locked(dentry);
spin_unlock(&dentry->d_lock);
}
static int scdrop_pagecache(const char * __user filename)
{
unsigned int lookup_flags = LOOKUP_FOLLOW;
struct path path;
int error;
retry:
error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
if (!error) {
/* clean */
clean_all_dentries(path.dentry);
}
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
return error;
}
static int scdrop_ctl_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
char __user *pathname = buffer + *lenp - 1;
put_user('\0', pathname);
if (!write)
return 0;
return scdrop_pagecache(buffer);
}
static struct ctl_path vm_path[] = { { .procname = "vm", }, { } };
static struct ctl_table scdrop_ctl_table[] = {
{
.procname = "sdrop_caches",
.mode = 0644,
.proc_handler = scdrop_ctl_handler,
},
{ }
};
static struct ctl_table_header *scdrop_proc_entry;
/* Init function called on module entry */
int scdrop_init(void)
{
int ret = 0;
scdrop_proc_entry = register_sysctl_paths(vm_path, scdrop_ctl_table);
if (scdrop_proc_entry == NULL) {
ret = -ENOMEM;
printk(KERN_INFO "%s: Couldn't create proc entry\n",
__FUNCTION__);
} else {
printk(KERN_INFO "%s: Module loaded.\n", __FUNCTION__);
}
return ret;
}
/* Cleanup function called on module exit */
void scdrop_cleanup(void)
{
unregister_sysctl_table(scdrop_proc_entry);
printk(KERN_INFO "%s called. Module is now unloaded.\n", __FUNCTION__);
return;
}
/* Declare entry and exit functions */
module_init(scdrop_init);
module_exit(scdrop_cleanup);