aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2016-06-17 17:51:17 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-08-31 08:48:52 -0400
commitdf6a58c5c5aa8ecb1e088ecead3fa33ae70181f1 (patch)
treeade704b2fc9a7efba12b2fe76f0ec6e0060d145b /fs
parent3eab887a55424fc2c27553b7bfe32330df83f7b8 (diff)
kernfs: don't depend on d_find_any_alias() when generating notifications
kernfs_notify_workfn() sends out file modified events for the scheduled kernfs_nodes. Because the modifications aren't from userland, it doesn't have the matching file struct at hand and can't use fsnotify_modify(). Instead, it looked up the inode and then used d_find_any_alias() to find the dentry and used fsnotify_parent() and fsnotify() directly to generate notifications. The assumption was that the relevant dentries would have been pinned if there are listeners, which isn't true as inotify doesn't pin dentries at all and watching the parent doesn't pin the child dentries even for dnotify. This led to, for example, inotify watchers not getting notifications if the system is under memory pressure and the matching dentries got reclaimed. It can also be triggered through /proc/sys/vm/drop_caches or a remount attempt which involves shrinking dcache. fsnotify_parent() only uses the dentry to access the parent inode, which kernfs can do easily. Update kernfs_notify_workfn() so that it uses fsnotify() directly for both the parent and target inodes without going through d_find_any_alias(). While at it, supply the target file name to fsnotify() from kernfs_node->name. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: Evgeny Vereshchagin <evvers@ya.ru> Fixes: d911d9874801 ("kernfs: make kernfs_notify() trigger inotify events too") Cc: John McCutchan <john@johnmccutchan.com> Cc: Robert Love <rlove@rlove.org> Cc: Eric Paris <eparis@parisplace.org> Cc: stable@vger.kernel.org # v3.16+ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/kernfs/file.c28
1 files changed, 21 insertions, 7 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index e1574008adc9..2bcb86e6e6ca 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -840,21 +840,35 @@ repeat:
840 mutex_lock(&kernfs_mutex); 840 mutex_lock(&kernfs_mutex);
841 841
842 list_for_each_entry(info, &kernfs_root(kn)->supers, node) { 842 list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
843 struct kernfs_node *parent;
843 struct inode *inode; 844 struct inode *inode;
844 struct dentry *dentry;
845 845
846 /*
847 * We want fsnotify_modify() on @kn but as the
848 * modifications aren't originating from userland don't
849 * have the matching @file available. Look up the inodes
850 * and generate the events manually.
851 */
846 inode = ilookup(info->sb, kn->ino); 852 inode = ilookup(info->sb, kn->ino);
847 if (!inode) 853 if (!inode)
848 continue; 854 continue;
849 855
850 dentry = d_find_any_alias(inode); 856 parent = kernfs_get_parent(kn);
851 if (dentry) { 857 if (parent) {
852 fsnotify_parent(NULL, dentry, FS_MODIFY); 858 struct inode *p_inode;
853 fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE, 859
854 NULL, 0); 860 p_inode = ilookup(info->sb, parent->ino);
855 dput(dentry); 861 if (p_inode) {
862 fsnotify(p_inode, FS_MODIFY | FS_EVENT_ON_CHILD,
863 inode, FSNOTIFY_EVENT_INODE, kn->name, 0);
864 iput(p_inode);
865 }
866
867 kernfs_put(parent);
856 } 868 }
857 869
870 fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
871 kn->name, 0);
858 iput(inode); 872 iput(inode);
859 } 873 }
860 874