From: Greg Kroah-Hartman Date: Fri, 7 Aug 2015 05:35:00 +0000 (-0700) Subject: 3.14-stable patches X-Git-Tag: v4.1.5~22 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bfa69a606addfee144c393a2215a385eaf105399;p=thirdparty%2Fkernel%2Fstable-queue.git 3.14-stable patches added patches: freeing-unlinked-file-indefinitely-delayed.patch --- diff --git a/queue-3.14/freeing-unlinked-file-indefinitely-delayed.patch b/queue-3.14/freeing-unlinked-file-indefinitely-delayed.patch new file mode 100644 index 00000000000..b331854611b --- /dev/null +++ b/queue-3.14/freeing-unlinked-file-indefinitely-delayed.patch @@ -0,0 +1,89 @@ +From 75a6f82a0d10ef8f13cd8fe7212911a0252ab99e Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Wed, 8 Jul 2015 02:42:38 +0100 +Subject: freeing unlinked file indefinitely delayed + +From: Al Viro + +commit 75a6f82a0d10ef8f13cd8fe7212911a0252ab99e upstream. + + Normally opening a file, unlinking it and then closing will have +the inode freed upon close() (provided that it's not otherwise busy and +has no remaining links, of course). However, there's one case where that +does *not* happen. Namely, if you open it by fhandle with cold dcache, +then unlink() and close(). + + In normal case you get d_delete() in unlink(2) notice that dentry +is busy and unhash it; on the final dput() it will be forcibly evicted from +dcache, triggering iput() and inode removal. In this case, though, we end +up with *two* dentries - disconnected (created by open-by-fhandle) and +regular one (used by unlink()). The latter will have its reference to inode +dropped just fine, but the former will not - it's considered hashed (it +is on the ->s_anon list), so it will stay around until the memory pressure +will finally do it in. As the result, we have the final iput() delayed +indefinitely. It's trivial to reproduce - + +void flush_dcache(void) +{ + system("mount -o remount,rw /"); +} + +static char buf[20 * 1024 * 1024]; + +main() +{ + int fd; + union { + struct file_handle f; + char buf[MAX_HANDLE_SZ]; + } x; + int m; + + x.f.handle_bytes = sizeof(x); + chdir("/root"); + mkdir("foo", 0700); + fd = open("foo/bar", O_CREAT | O_RDWR, 0600); + close(fd); + name_to_handle_at(AT_FDCWD, "foo/bar", &x.f, &m, 0); + flush_dcache(); + fd = open_by_handle_at(AT_FDCWD, &x.f, O_RDWR); + unlink("foo/bar"); + write(fd, buf, sizeof(buf)); + system("df ."); /* 20Mb eaten */ + close(fd); + system("df ."); /* should've freed those 20Mb */ + flush_dcache(); + system("df ."); /* should be the same as #2 */ +} + +will spit out something like +Filesystem 1K-blocks Used Available Use% Mounted on +/dev/root 322023 303843 1131 100% / +Filesystem 1K-blocks Used Available Use% Mounted on +/dev/root 322023 303843 1131 100% / +Filesystem 1K-blocks Used Available Use% Mounted on +/dev/root 322023 283282 21692 93% / +- inode gets freed only when dentry is finally evicted (here we trigger +than by remount; normally it would've happened in response to memory +pressure hell knows when). + +Acked-by: J. Bruce Fields +Signed-off-by: Al Viro +Signed-off-by: Greg Kroah-Hartman + +--- + fs/dcache.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -587,6 +587,9 @@ repeat: + if (unlikely(d_unhashed(dentry))) + goto kill_it; + ++ if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED)) ++ goto kill_it; ++ + if (unlikely(dentry->d_flags & DCACHE_OP_DELETE)) { + if (dentry->d_op->d_delete(dentry)) + goto kill_it; diff --git a/queue-3.14/series b/queue-3.14/series index ff962f6c255..f858ec4eec4 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -1 +1,2 @@ mm-avoid-setting-up-anonymous-pages-into-file-mapping.patch +freeing-unlinked-file-indefinitely-delayed.patch