fsnotify: place marks on object in order of group memory address
Eric Paris [Wed, 28 Jul 2010 14:18:38 +0000 (10:18 -0400)]
fsnotify_marks currently are placed on objects (inodes or vfsmounts) in
arbitrary order.  This patch places them in order of the group memory address.

Signed-off-by: Eric Paris <eparis@redhat.com>

fs/notify/inode_mark.c
fs/notify/vfsmount_mark.c

index 0c0a48b..83ce6db 100644 (file)
@@ -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;
 }
 
index ec580a2..c4b3f14 100644 (file)
@@ -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;