summaryrefslogtreecommitdiffstats
path: root/fs/notify/fsnotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/notify/fsnotify.c')
-rw-r--r--fs/notify/fsnotify.c138
1 files changed, 83 insertions, 55 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 613ec7e5a465..f174397b63a0 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -184,8 +184,6 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask
EXPORT_SYMBOL_GPL(__fsnotify_parent);
static int send_to_group(struct inode *to_tell,
- struct fsnotify_mark *inode_mark,
- struct fsnotify_mark *vfsmount_mark,
__u32 mask, const void *data,
int data_is, u32 cookie,
const unsigned char *file_name,
@@ -195,48 +193,45 @@ static int send_to_group(struct inode *to_tell,
__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
__u32 marks_mask = 0;
__u32 marks_ignored_mask = 0;
+ struct fsnotify_mark *mark;
+ int type;
- if (unlikely(!inode_mark && !vfsmount_mark)) {
- BUG();
+ if (WARN_ON(!iter_info->report_mask))
return 0;
- }
/* clear ignored on inode modification */
if (mask & FS_MODIFY) {
- if (inode_mark &&
- !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
- inode_mark->ignored_mask = 0;
- if (vfsmount_mark &&
- !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
- vfsmount_mark->ignored_mask = 0;
- }
-
- /* does the inode mark tell us to do something? */
- if (inode_mark) {
- group = inode_mark->group;
- marks_mask |= inode_mark->mask;
- marks_ignored_mask |= inode_mark->ignored_mask;
+ fsnotify_foreach_obj_type(type) {
+ if (!fsnotify_iter_should_report_type(iter_info, type))
+ continue;
+ mark = iter_info->marks[type];
+ if (mark &&
+ !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
+ mark->ignored_mask = 0;
+ }
}
- /* does the vfsmount_mark tell us to do something? */
- if (vfsmount_mark) {
- group = vfsmount_mark->group;
- marks_mask |= vfsmount_mark->mask;
- marks_ignored_mask |= vfsmount_mark->ignored_mask;
+ fsnotify_foreach_obj_type(type) {
+ if (!fsnotify_iter_should_report_type(iter_info, type))
+ continue;
+ mark = iter_info->marks[type];
+ /* does the object mark tell us to do something? */
+ if (mark) {
+ group = mark->group;
+ marks_mask |= mark->mask;
+ marks_ignored_mask |= mark->ignored_mask;
+ }
}
- pr_debug("%s: group=%p to_tell=%p mask=%x inode_mark=%p"
- " vfsmount_mark=%p marks_mask=%x marks_ignored_mask=%x"
+ pr_debug("%s: group=%p to_tell=%p mask=%x marks_mask=%x marks_ignored_mask=%x"
" data=%p data_is=%d cookie=%d\n",
- __func__, group, to_tell, mask, inode_mark, vfsmount_mark,
- marks_mask, marks_ignored_mask, data,
- data_is, cookie);
+ __func__, group, to_tell, mask, marks_mask, marks_ignored_mask,
+ data, data_is, cookie);
if (!(test_mask & marks_mask & ~marks_ignored_mask))
return 0;
- return group->ops->handle_event(group, to_tell, inode_mark,
- vfsmount_mark, mask, data, data_is,
+ return group->ops->handle_event(group, to_tell, mask, data, data_is,
file_name, cookie, iter_info);
}
@@ -264,6 +259,57 @@ static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
}
/*
+ * iter_info is a multi head priority queue of marks.
+ * Pick a subset of marks from queue heads, all with the
+ * same group and set the report_mask for selected subset.
+ * Returns the report_mask of the selected subset.
+ */
+static unsigned int fsnotify_iter_select_report_types(
+ struct fsnotify_iter_info *iter_info)
+{
+ struct fsnotify_group *max_prio_group = NULL;
+ struct fsnotify_mark *mark;
+ int type;
+
+ /* Choose max prio group among groups of all queue heads */
+ fsnotify_foreach_obj_type(type) {
+ mark = iter_info->marks[type];
+ if (mark &&
+ fsnotify_compare_groups(max_prio_group, mark->group) > 0)
+ max_prio_group = mark->group;
+ }
+
+ if (!max_prio_group)
+ return 0;
+
+ /* Set the report mask for marks from same group as max prio group */
+ iter_info->report_mask = 0;
+ fsnotify_foreach_obj_type(type) {
+ mark = iter_info->marks[type];
+ if (mark &&
+ fsnotify_compare_groups(max_prio_group, mark->group) == 0)
+ fsnotify_iter_set_report_type(iter_info, type);
+ }
+
+ return iter_info->report_mask;
+}
+
+/*
+ * Pop from iter_info multi head queue, the marks that were iterated in the
+ * current iteration step.
+ */
+static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
+{
+ int type;
+
+ fsnotify_foreach_obj_type(type) {
+ if (fsnotify_iter_should_report_type(iter_info, type))
+ iter_info->marks[type] =
+ fsnotify_next_mark(iter_info->marks[type]);
+ }
+}
+
+/*
* This is the main call to fsnotify. The VFS calls into hook specific functions
* in linux/fsnotify.h. Those functions then in turn call here. Here will call
* out to all of the registered fsnotify_group. Those groups can then use the
@@ -307,15 +353,15 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
if ((mask & FS_MODIFY) ||
(test_mask & to_tell->i_fsnotify_mask)) {
- iter_info.inode_mark =
+ iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&to_tell->i_fsnotify_marks);
}
if (mnt && ((mask & FS_MODIFY) ||
(test_mask & mnt->mnt_fsnotify_mask))) {
- iter_info.inode_mark =
+ iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&to_tell->i_fsnotify_marks);
- iter_info.vfsmount_mark =
+ iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
}
@@ -324,32 +370,14 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
* ignore masks are properly reflected for mount mark notifications.
* That's why this traversal is so complicated...
*/
- while (iter_info.inode_mark || iter_info.vfsmount_mark) {
- struct fsnotify_mark *inode_mark = iter_info.inode_mark;
- struct fsnotify_mark *vfsmount_mark = iter_info.vfsmount_mark;
-
- if (inode_mark && vfsmount_mark) {
- int cmp = fsnotify_compare_groups(inode_mark->group,
- vfsmount_mark->group);
- if (cmp > 0)
- inode_mark = NULL;
- else if (cmp < 0)
- vfsmount_mark = NULL;
- }
-
- ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
- data, data_is, cookie, file_name,
- &iter_info);
+ while (fsnotify_iter_select_report_types(&iter_info)) {
+ ret = send_to_group(to_tell, mask, data, data_is, cookie,
+ file_name, &iter_info);
if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
goto out;
- if (inode_mark)
- iter_info.inode_mark =
- fsnotify_next_mark(iter_info.inode_mark);
- if (vfsmount_mark)
- iter_info.vfsmount_mark =
- fsnotify_next_mark(iter_info.vfsmount_mark);
+ fsnotify_iter_next(&iter_info);
}
ret = 0;
out: