summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/inode_mark.c40
-rw-r--r--fs/notify/vfsmount_mark.c40
2 files changed, 55 insertions, 25 deletions
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 0c0a48b1659f..83ce6db34039 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -174,15 +174,17 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark,
}
/*
- * Attach an initialized mark to a given group and inode.
+ * Attach an initialized mark to a given inode.
* These marks may be used for the fsnotify backend to determine which
- * event types should be delivered to which group and for which inodes.
+ * event types should be delivered to which group and for which inodes. These
+ * marks are ordered according to the group's location in memory.
*/
int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group, struct inode *inode,
int allow_dups)
{
- struct fsnotify_mark *lmark = NULL;
+ struct fsnotify_mark *lmark;
+ struct hlist_node *node, *last = NULL;
int ret = 0;
mark->flags = FSNOTIFY_MARK_FLAG_INODE;
@@ -192,21 +194,37 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
spin_lock(&inode->i_lock);
- if (!allow_dups)
- lmark = fsnotify_find_inode_mark_locked(group, inode);
- if (!lmark) {
- mark->i.inode = inode;
+ mark->i.inode = inode;
+ /* is mark the first mark? */
+ if (hlist_empty(&inode->i_fsnotify_marks)) {
hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks);
+ goto out;
+ }
+
+ /* should mark be in the middle of the current list? */
+ hlist_for_each_entry(lmark, node, &inode->i_fsnotify_marks, i.i_list) {
+ last = node;
+
+ if ((lmark->group == group) && !allow_dups) {
+ ret = -EEXIST;
+ goto out;
+ }
- fsnotify_recalc_inode_mask_locked(inode);
+ if (mark->group < lmark->group)
+ continue;
+
+ hlist_add_before(&mark->i.i_list, &lmark->i.i_list);
+ goto out;
}
+ BUG_ON(last == NULL);
+ /* mark should be the last entry. last is the current last entry */
+ hlist_add_after(last, &mark->i.i_list);
+out:
+ fsnotify_recalc_inode_mask_locked(inode);
spin_unlock(&inode->i_lock);
- if (lmark)
- ret = -EEXIST;
-
return ret;
}
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
index ec580a25d293..c4b3f14d2530 100644
--- a/fs/notify/vfsmount_mark.c
+++ b/fs/notify/vfsmount_mark.c
@@ -141,34 +141,46 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group, struct vfsmount *mnt,
int allow_dups)
{
- struct fsnotify_mark *lmark = NULL;
+ struct fsnotify_mark *lmark;
+ struct hlist_node *node, *last = NULL;
int ret = 0;
mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT;
- /*
- * LOCKING ORDER!!!!
- * mark->lock
- * group->mark_lock
- * mnt->mnt_root->d_lock
- */
assert_spin_locked(&mark->lock);
assert_spin_locked(&group->mark_lock);
spin_lock(&mnt->mnt_root->d_lock);
- if (!allow_dups)
- lmark = fsnotify_find_vfsmount_mark_locked(group, mnt);
- if (!lmark) {
- mark->m.mnt = mnt;
+ mark->m.mnt = mnt;
+ /* is mark the first mark? */
+ if (hlist_empty(&mnt->mnt_fsnotify_marks)) {
hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks);
+ goto out;
+ }
+
+ /* should mark be in the middle of the current list? */
+ hlist_for_each_entry(lmark, node, &mnt->mnt_fsnotify_marks, m.m_list) {
+ last = node;
+
+ if ((lmark->group == group) && !allow_dups) {
+ ret = -EEXIST;
+ goto out;
+ }
+
+ if (mark->group < lmark->group)
+ continue;
- fsnotify_recalc_vfsmount_mask_locked(mnt);
- } else {
- ret = -EEXIST;
+ hlist_add_before(&mark->m.m_list, &lmark->m.m_list);
+ goto out;
}
+ BUG_ON(last == NULL);
+ /* mark should be the last entry. last is the current last entry */
+ hlist_add_after(last, &mark->m.m_list);
+out:
+ fsnotify_recalc_vfsmount_mask_locked(mnt);
spin_unlock(&mnt->mnt_root->d_lock);
return ret;