fsnotify: take groups mark_lock before mark lock
authorLino Sanfilippo <LinoSanfilippo@gmx.de>
Tue, 14 Jun 2011 15:29:48 +0000 (17:29 +0200)
committerEric Paris <eparis@redhat.com>
Tue, 11 Dec 2012 18:29:45 +0000 (13:29 -0500)
Race-free addition and removal of a mark to a groups mark list would be easier
if we could lock the mark list of group before we lock the specific mark.
This patch changes the order used to add/remove marks to/from mark lists from

1. mark->lock
2. group->mark_lock
3. inode->i_lock

to

1. group->mark_lock
2. mark->lock
3. inode->i_lock

Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Signed-off-by: Eric Paris <eparis@redhat.com>
fs/notify/mark.c

index 3c7a169..32447dc 100644 (file)
@@ -127,20 +127,27 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
        struct inode *inode = NULL;
 
        spin_lock(&mark->lock);
-
+       /* dont get the group from a mark that is not alive yet */
+       if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
+               spin_unlock(&mark->lock);
+               return;
+       }
        fsnotify_get_group(mark->group);
        group = mark->group;
+       spin_unlock(&mark->lock);
+
+       spin_lock(&group->mark_lock);
+       spin_lock(&mark->lock);
 
        /* something else already called this function on this mark */
        if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) {
                spin_unlock(&mark->lock);
+               spin_unlock(&group->mark_lock);
                goto put_group;
        }
 
        mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE;
 
-       spin_lock(&group->mark_lock);
-
        if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
                inode = mark->i.inode;
                fsnotify_destroy_inode_mark(mark);
@@ -151,8 +158,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
 
        list_del_init(&mark->g_list);
 
-       spin_unlock(&group->mark_lock);
        spin_unlock(&mark->lock);
+       spin_unlock(&group->mark_lock);
 
        spin_lock(&destroy_lock);
        list_add(&mark->destroy_list, &destroy_list);
@@ -225,13 +232,13 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
 
        /*
         * LOCKING ORDER!!!!
-        * mark->lock
         * group->mark_lock
+        * mark->lock
         * inode->i_lock
         */
-       spin_lock(&mark->lock);
        spin_lock(&group->mark_lock);
 
+       spin_lock(&mark->lock);
        mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE;
 
        fsnotify_get_group(group);
@@ -252,13 +259,12 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
                BUG();
        }
 
-       spin_unlock(&group->mark_lock);
-
        /* this will pin the object if appropriate */
        fsnotify_set_mark_mask_locked(mark, mark->mask);
-
        spin_unlock(&mark->lock);
 
+       spin_unlock(&group->mark_lock);
+
        if (inode)
                __fsnotify_update_child_dentry_flags(inode);
 
@@ -270,8 +276,8 @@ err:
        mark->group = NULL;
        atomic_dec(&group->num_marks);
 
-       spin_unlock(&group->mark_lock);
        spin_unlock(&mark->lock);
+       spin_unlock(&group->mark_lock);
 
        spin_lock(&destroy_lock);
        list_add(&mark->destroy_list, &destroy_list);