[Linux-cachefs] [PATCH v3] cachefiles: Page leaking in cachefiles_read_backing_file while vmscan is active

Daniel Axtens dja at axtens.net
Mon Sep 24 02:02:39 UTC 2018


From: Kiran Kumar Modukuri <kiran.modukuri at gmail.com>

[Description]
In a heavily loaded system where the system pagecache is nearing memory limits and fscache is enabled,
pages can be leaked by fscache while trying read pages from cachefiles backend.
This can happen because two applications can be reading same page from a single mount,
two threads can be trying to read the backing page at same time. This results in one of the thread
finding that a page for the backing file or netfs file is already in the radix tree. During the error
handling cachefiles does not cleanup the reference on backing page, leading to page leak.

[Fix]
The fix is straightforward, to decrement the reference when error is encounterd.

[Testing]
I have tested the fix using following method for 12+ hrs.

1) mkdir -p /mnt/nfs ; mount -o vers=3,fsc <server_ip>:/export /mnt/nfs
2) create 10000 files of 2.8MB in a NFS mount.
3) start a thread to simulate heavy VM presssure
   (while true ; do echo 3 > /proc/sys/vm/drop_caches ; sleep 1 ; done)&
4) start multiple parallel reader for data set at same time
   find /mnt/nfs -type f | xargs -P 80 cat > /dev/null &
   find /mnt/nfs -type f | xargs -P 80 cat > /dev/null &
   find /mnt/nfs -type f | xargs -P 80 cat > /dev/null &
   ..
   ..
   find /mnt/nfs -type f | xargs -P 80 cat > /dev/null &
   find /mnt/nfs -type f | xargs -P 80 cat > /dev/null &
5) finally check using cat /proc/fs/fscache/stats | grep -i pages ;
   free -h , cat /proc/meminfo and page-types -r -b lru
   to ensure all pages are freed.

Reviewed-by: Daniel Axtens <dja at axtens.net>
Signed-off-by: Shantanu Goel <sgoel01 at yahoo.com>
Signed-off-by: Kiran Kumar Modukuri <kiran.modukuri at gmail.com>
[dja: forward ported to current upstream]
Signed-off-by: Daniel Axtens <dja at axtens.net>

---

v3: In an attempt to get the fix out to users, I proposed it to the
Canonical kernel team. This version is slightly narrowed in scope
in response to their feedback. It also sets free'd pointers to NULL
explictly in more cases.
---
 fs/cachefiles/rdwr.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c
index 40f7595aad10..8bf076eb10e2 100644
--- a/fs/cachefiles/rdwr.c
+++ b/fs/cachefiles/rdwr.c
@@ -511,6 +511,8 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
 				goto installed_new_backing_page;
 			if (ret != -EEXIST)
 				goto nomem;
+			put_page(newpage);
+			newpage = NULL;
 		}
 
 		/* we've installed a new backing page, so now we need
@@ -535,7 +537,10 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
 					    netpage->index, cachefiles_gfp);
 		if (ret < 0) {
 			if (ret == -EEXIST) {
+				put_page(backpage);
+				backpage = NULL;
 				put_page(netpage);
+				netpage = NULL;
 				fscache_retrieval_complete(op, 1);
 				continue;
 			}
@@ -608,7 +613,10 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object,
 					    netpage->index, cachefiles_gfp);
 		if (ret < 0) {
 			if (ret == -EEXIST) {
+				put_page(backpage);
+				backpage = NULL;
 				put_page(netpage);
+				netpage = NULL;
 				fscache_retrieval_complete(op, 1);
 				continue;
 			}
-- 
2.17.1




More information about the Linux-cachefs mailing list