To clean the page cache one can use /proc/sys/vm/drop_caches. But this
drops the whole page cache. In contrast to that sdrop_caches enables
ability to drop the page cache selectively by path string.

Suggested-by: Thomas Knauth <thomas.kna...@gmx.de>
Signed-off-by: Maksym Planeta <mcsim.plan...@gmail.com>
---
 Documentation/sysctl/vm.txt |  15 ++++++
 fs/Makefile                 |   2 +-
 fs/sdrop_caches.c           | 124 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 140 insertions(+), 1 deletion(-)
 create mode 100644 fs/sdrop_caches.c

diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index bd4b34c..faad01d 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -28,6 +28,7 @@ Currently, these files are in /proc/sys/vm:
 - dirty_ratio
 - dirty_writeback_centisecs
 - drop_caches
+- sdrop_caches
 - extfrag_threshold
 - hugepages_treat_as_movable
 - hugetlb_shm_group
@@ -211,6 +212,20 @@ with your system.  To disable them, echo 4 (bit 3) into 
drop_caches.
 
 ==============================================================
 
+sdrop_caches
+
+Writing to this will cause the kernel to drop clean caches starting from
+specified path.
+
+To free pagecache of a file:
+       echo /home/user/file > /proc/sys/vm/sdrop_caches
+To free pagecache of a directory and all files in it.
+       echo /home/user/directly > /proc/sys/vm/sdrop_caches
+
+Restrictions are the same as for drop_caches.
+
+==============================================================
+
 extfrag_threshold
 
 This parameter affects whether the kernel will compact memory or direct
diff --git a/fs/Makefile b/fs/Makefile
index 4030cbf..366c7b9 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -44,7 +44,7 @@ obj-$(CONFIG_FS_MBCACHE)      += mbcache.o
 obj-$(CONFIG_FS_POSIX_ACL)     += posix_acl.o
 obj-$(CONFIG_NFS_COMMON)       += nfs_common/
 obj-$(CONFIG_COREDUMP)         += coredump.o
-obj-$(CONFIG_SYSCTL)           += drop_caches.o
+obj-$(CONFIG_SYSCTL)           += drop_caches.o sdrop_caches.o
 
 obj-$(CONFIG_FHANDLE)          += fhandle.o
 
diff --git a/fs/sdrop_caches.c b/fs/sdrop_caches.c
new file mode 100644
index 0000000..c193655
--- /dev/null
+++ b/fs/sdrop_caches.c
@@ -0,0 +1,124 @@
+/*
+ * 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 <linux/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>
+
+static void clean_mapping(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+
+       if (!inode)
+               return;
+
+       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 drop_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 sdrop_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 drop_pagecache(buffer);
+}
+
+static struct ctl_path vm_path[] = { { .procname = "vm", }, { } };
+static struct ctl_table sdrop_ctl_table[] = {
+       {
+               .procname = "sdrop_caches",
+               .mode = 0644,
+               .proc_handler = sdrop_ctl_handler,
+       },
+       { }
+};
+
+static struct ctl_table_header *sdrop_proc_entry;
+
+/* Init function called on module entry */
+int sdrop_init(void)
+{
+       int ret = 0;
+
+       sdrop_proc_entry = register_sysctl_paths(vm_path, sdrop_ctl_table);
+
+       if (sdrop_proc_entry == NULL) {
+               ret = -ENOMEM;
+               pr_err("sdrop_caches: Couldn't create proc entry\n");
+       }
+
+       return ret;
+}
+
+/* Cleanup function called on module exit */
+void sdrop_cleanup(void)
+{
+       unregister_sysctl_table(sdrop_proc_entry);
+}
+
+module_init(sdrop_init);
+module_exit(sdrop_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Selective pagecache drop module");
+MODULE_AUTHOR("Maksym Planeta");
-- 
2.0.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to