summaryrefslogtreecommitdiff
path: root/fs/notify/fsnotify.c
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2010-07-28 18:18:39 +0400
committerEric Paris <eparis@redhat.com>2010-07-28 18:18:54 +0400
commit613a807fe7c793ceb7d6f059773527a5a6c84a96 (patch)
treed217dbde37c8c0a145c36dea24e7afe5fe5e5ff0 /fs/notify/fsnotify.c
parent84a5b68e8da1490906c11129756490a556ae2c19 (diff)
downloadlinux-613a807fe7c793ceb7d6f059773527a5a6c84a96.tar.xz
fsnotify: walk the inode and vfsmount lists simultaneously
We currently walk the list of marks on an inode followed by the list of marks on the vfsmount. These are in order (by the memory address of the group) so lets walk them both together. Eventually we can pass both the inode mark and the vfsmount mark to helpers simultaneously. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r--fs/notify/fsnotify.c134
1 files changed, 84 insertions, 50 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 0bb4aeb8e00f..cdaa51cb698c 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -140,19 +140,31 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
}
EXPORT_SYMBOL_GPL(__fsnotify_parent);
-static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
- struct vfsmount *mnt, struct fsnotify_mark *mark,
- __u32 mask, void *data, int data_is, u32 cookie,
+static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
+ struct fsnotify_mark *mark,
+ __u32 mask, void *data,
+ int data_is, u32 cookie,
const unsigned char *file_name,
struct fsnotify_event **event)
{
+ struct fsnotify_group *group = mark->group;
+ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
+
pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p"
" data_is=%d cookie=%d event=%p\n", __func__, group, to_tell,
mnt, mark, mask, data, data_is, cookie, *event);
+ if ((mask & FS_MODIFY) &&
+ !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
+ mark->ignored_mask = 0;
+
+ if (!(test_mask & mark->mask & ~mark->ignored_mask))
+ return 0;
+
if (group->ops->should_send_event(group, to_tell, mnt, mark, mask,
data, data_is) == false)
return 0;
+
if (!*event) {
*event = fsnotify_create_event(to_tell, mask, data,
data_is, file_name,
@@ -172,67 +184,89 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell,
int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const unsigned char *file_name, u32 cookie)
{
- struct fsnotify_mark *mark;
- struct fsnotify_group *group;
+ struct hlist_node *inode_node, *vfsmount_node;
+ struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
+ struct fsnotify_group *inode_group, *vfsmount_group;
struct fsnotify_event *event = NULL;
- struct hlist_node *node;
- struct vfsmount *mnt = NULL;
+ struct vfsmount *mnt;
int idx, ret = 0;
+ bool used_inode = false, used_vfsmount = false;
/* global tests shouldn't care about events on child only the specific event */
__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
if (data_is == FSNOTIFY_EVENT_FILE)
mnt = ((struct file *)data)->f_path.mnt;
+ else
+ mnt = NULL;
+
+ /*
+ * if this is a modify event we may need to clear the ignored masks
+ * otherwise return if neither the inode nor the vfsmount care about
+ * this type of event.
+ */
+ if (!(mask & FS_MODIFY) &&
+ !(test_mask & to_tell->i_fsnotify_mask) &&
+ !(mnt && test_mask & mnt->mnt_fsnotify_mask))
+ return 0;
idx = srcu_read_lock(&fsnotify_mark_srcu);
- if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) {
- hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) {
-
- pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n",
- __func__, mark, mark->mask, mark->ignored_mask);
-
- if ((mask & FS_MODIFY) &&
- !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
- mark->ignored_mask = 0;
-
- if (test_mask & mark->mask & ~mark->ignored_mask) {
- group = mark->group;
- if (!group)
- continue;
- ret = send_to_group(group, to_tell, NULL, mark, mask,
- data, data_is, cookie, file_name,
- &event);
- if (ret)
- goto out;
- }
- }
+ if ((mask & FS_MODIFY) ||
+ (test_mask & to_tell->i_fsnotify_mask))
+ inode_node = to_tell->i_fsnotify_marks.first;
+ else
+ inode_node = NULL;
+
+ if (mnt) {
+ if ((mask & FS_MODIFY) ||
+ (test_mask & mnt->mnt_fsnotify_mask))
+ vfsmount_node = mnt->mnt_fsnotify_marks.first;
+ else
+ vfsmount_node = NULL;
+ } else {
+ mnt = NULL;
+ vfsmount_node = NULL;
}
- if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) ||
- (mask & FS_MODIFY))) {
- hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) {
-
- pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n",
- __func__, mark, mark->mask, mark->ignored_mask);
-
- if ((mask & FS_MODIFY) &&
- !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
- mark->ignored_mask = 0;
-
- if (test_mask & mark->mask & ~mark->ignored_mask) {
- group = mark->group;
- if (!group)
- continue;
- ret = send_to_group(group, to_tell, mnt, mark, mask,
- data, data_is, cookie, file_name,
- &event);
- if (ret)
- goto out;
- }
+ while (inode_node || vfsmount_node) {
+ if (inode_node) {
+ inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
+ struct fsnotify_mark, i.i_list);
+ inode_group = inode_mark->group;
+ } else
+ inode_group = (void *)-1;
+
+ if (vfsmount_node) {
+ vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
+ struct fsnotify_mark, m.m_list);
+ vfsmount_group = vfsmount_mark->group;
+ } else
+ vfsmount_group = (void *)-1;
+
+ if (inode_group < vfsmount_group) {
+ /* handle inode */
+ send_to_group(to_tell, NULL, inode_mark, mask, data,
+ data_is, cookie, file_name, &event);
+ used_inode = true;
+ } else if (vfsmount_group < inode_group) {
+ send_to_group(to_tell, mnt, vfsmount_mark, mask, data,
+ data_is, cookie, file_name, &event);
+ used_vfsmount = true;
+ } else {
+ send_to_group(to_tell, mnt, vfsmount_mark, mask, data,
+ data_is, cookie, file_name, &event);
+ used_vfsmount = true;
+ send_to_group(to_tell, NULL, inode_mark, mask, data,
+ data_is, cookie, file_name, &event);
+ used_inode = true;
}
+
+ if (used_inode)
+ inode_node = inode_node->next;
+ if (used_vfsmount)
+ vfsmount_node = vfsmount_node->next;
}
-out:
+
srcu_read_unlock(&fsnotify_mark_srcu, idx);
/*
* fsnotify_create_event() took a reference so the event can't be cleaned