Commit 613a807f authored by Eric Paris's avatar Eric Paris

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: default avatarEric Paris <eparis@redhat.com>
parent 84a5b68e
...@@ -140,19 +140,31 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) ...@@ -140,19 +140,31 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
} }
EXPORT_SYMBOL_GPL(__fsnotify_parent); EXPORT_SYMBOL_GPL(__fsnotify_parent);
static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, static int send_to_group(struct inode *to_tell, struct vfsmount *mnt,
struct vfsmount *mnt, struct fsnotify_mark *mark, struct fsnotify_mark *mark,
__u32 mask, void *data, int data_is, u32 cookie, __u32 mask, void *data,
int data_is, u32 cookie,
const unsigned char *file_name, const unsigned char *file_name,
struct fsnotify_event **event) 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" 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, " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell,
mnt, mark, mask, data, data_is, cookie, *event); 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, if (group->ops->should_send_event(group, to_tell, mnt, mark, mask,
data, data_is) == false) data, data_is) == false)
return 0; return 0;
if (!*event) { if (!*event) {
*event = fsnotify_create_event(to_tell, mask, data, *event = fsnotify_create_event(to_tell, mask, data,
data_is, file_name, data_is, file_name,
...@@ -172,67 +184,89 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, ...@@ -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, int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const unsigned char *file_name, u32 cookie) const unsigned char *file_name, u32 cookie)
{ {
struct fsnotify_mark *mark; struct hlist_node *inode_node, *vfsmount_node;
struct fsnotify_group *group; struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL;
struct fsnotify_group *inode_group, *vfsmount_group;
struct fsnotify_event *event = NULL; struct fsnotify_event *event = NULL;
struct hlist_node *node; struct vfsmount *mnt;
struct vfsmount *mnt = NULL;
int idx, ret = 0; int idx, ret = 0;
bool used_inode = false, used_vfsmount = false;
/* global tests shouldn't care about events on child only the specific event */ /* global tests shouldn't care about events on child only the specific event */
__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
if (data_is == FSNOTIFY_EVENT_FILE) if (data_is == FSNOTIFY_EVENT_FILE)
mnt = ((struct file *)data)->f_path.mnt; 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); idx = srcu_read_lock(&fsnotify_mark_srcu);
if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) { if ((mask & FS_MODIFY) ||
hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { (test_mask & to_tell->i_fsnotify_mask))
inode_node = to_tell->i_fsnotify_marks.first;
pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", else
__func__, mark, mark->mask, mark->ignored_mask); inode_node = NULL;
if ((mask & FS_MODIFY) && if (mnt) {
!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) if ((mask & FS_MODIFY) ||
mark->ignored_mask = 0; (test_mask & mnt->mnt_fsnotify_mask))
vfsmount_node = mnt->mnt_fsnotify_marks.first;
if (test_mask & mark->mask & ~mark->ignored_mask) { else
group = mark->group; vfsmount_node = NULL;
if (!group) } else {
continue; mnt = NULL;
ret = send_to_group(group, to_tell, NULL, mark, mask, vfsmount_node = NULL;
data, data_is, cookie, file_name,
&event);
if (ret)
goto out;
}
}
} }
if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) || while (inode_node || vfsmount_node) {
(mask & FS_MODIFY))) { if (inode_node) {
hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
struct fsnotify_mark, i.i_list);
pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", inode_group = inode_mark->group;
__func__, mark, mark->mask, mark->ignored_mask); } else
inode_group = (void *)-1;
if ((mask & FS_MODIFY) &&
!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) if (vfsmount_node) {
mark->ignored_mask = 0; vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu),
struct fsnotify_mark, m.m_list);
if (test_mask & mark->mask & ~mark->ignored_mask) { vfsmount_group = vfsmount_mark->group;
group = mark->group; } else
if (!group) vfsmount_group = (void *)-1;
continue;
ret = send_to_group(group, to_tell, mnt, mark, mask, if (inode_group < vfsmount_group) {
data, data_is, cookie, file_name, /* handle inode */
&event); send_to_group(to_tell, NULL, inode_mark, mask, data,
if (ret) data_is, cookie, file_name, &event);
goto out; 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); srcu_read_unlock(&fsnotify_mark_srcu, idx);
/* /*
* fsnotify_create_event() took a reference so the event can't be cleaned * fsnotify_create_event() took a reference so the event can't be cleaned
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment