fsnotify: clean up fsnotify_prepare/finish_user_wait()
authorMiklos Szeredi <mszeredi@redhat.com>
Mon, 30 Oct 2017 20:14:55 +0000 (21:14 +0100)
committerJan Kara <jack@suse.cz>
Tue, 31 Oct 2017 16:54:56 +0000 (17:54 +0100)
This patch doesn't actually fix any bug, just paves the way for fixing mark
and group pinning.

Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Cc: <stable@vger.kernel.org> # v4.12
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/mark.c

index 47a827975b5878b3052d83cf9467cf00ebaecf32..7ecd15add6a7f86d54db5ab21c425b3f49be9ac0 100644 (file)
@@ -109,16 +109,6 @@ void fsnotify_get_mark(struct fsnotify_mark *mark)
        atomic_inc(&mark->refcnt);
 }
 
-/*
- * Get mark reference when we found the mark via lockless traversal of object
- * list. Mark can be already removed from the list by now and on its way to be
- * destroyed once SRCU period ends.
- */
-static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark)
-{
-       return atomic_inc_not_zero(&mark->refcnt);
-}
-
 static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
 {
        u32 new_mask = 0;
@@ -256,32 +246,63 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
                           FSNOTIFY_REAPER_DELAY);
 }
 
-bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info)
+/*
+ * Get mark reference when we found the mark via lockless traversal of object
+ * list. Mark can be already removed from the list by now and on its way to be
+ * destroyed once SRCU period ends.
+ *
+ * Also pin the group so it doesn't disappear under us.
+ */
+static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark)
 {
        struct fsnotify_group *group;
 
-       if (WARN_ON_ONCE(!iter_info->inode_mark && !iter_info->vfsmount_mark))
-               return false;
-
-       if (iter_info->inode_mark)
-               group = iter_info->inode_mark->group;
-       else
-               group = iter_info->vfsmount_mark->group;
+       if (!mark)
+               return true;
 
+       group = mark->group;
        /*
         * Since acquisition of mark reference is an atomic op as well, we can
         * be sure this inc is seen before any effect of refcount increment.
         */
        atomic_inc(&group->user_waits);
+       if (atomic_inc_not_zero(&mark->refcnt))
+               return true;
+
+       if (atomic_dec_and_test(&group->user_waits) && group->shutdown)
+               wake_up(&group->notification_waitq);
+
+       return false;
+}
+
+/*
+ * Puts marks and wakes up group destruction if necessary.
+ *
+ * Pairs with fsnotify_get_mark_safe()
+ */
+static void fsnotify_put_mark_wake(struct fsnotify_mark *mark)
+{
+       if (mark) {
+               struct fsnotify_group *group = mark->group;
 
-       if (iter_info->inode_mark) {
-               /* This can fail if mark is being removed */
-               if (!fsnotify_get_mark_safe(iter_info->inode_mark))
-                       goto out_wait;
+               fsnotify_put_mark(mark);
+               /*
+                * We abuse notification_waitq on group shutdown for waiting for
+                * all marks pinned when waiting for userspace.
+                */
+               if (atomic_dec_and_test(&group->user_waits) && group->shutdown)
+                       wake_up(&group->notification_waitq);
        }
-       if (iter_info->vfsmount_mark) {
-               if (!fsnotify_get_mark_safe(iter_info->vfsmount_mark))
-                       goto out_inode;
+}
+
+bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info)
+{
+       /* This can fail if mark is being removed */
+       if (!fsnotify_get_mark_safe(iter_info->inode_mark))
+               return false;
+       if (!fsnotify_get_mark_safe(iter_info->vfsmount_mark)) {
+               fsnotify_put_mark_wake(iter_info->inode_mark);
+               return false;
        }
 
        /*
@@ -292,34 +313,13 @@ bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info)
        srcu_read_unlock(&fsnotify_mark_srcu, iter_info->srcu_idx);
 
        return true;
-out_inode:
-       if (iter_info->inode_mark)
-               fsnotify_put_mark(iter_info->inode_mark);
-out_wait:
-       if (atomic_dec_and_test(&group->user_waits) && group->shutdown)
-               wake_up(&group->notification_waitq);
-       return false;
 }
 
 void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info)
 {
-       struct fsnotify_group *group = NULL;
-
        iter_info->srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
-       if (iter_info->inode_mark) {
-               group = iter_info->inode_mark->group;
-               fsnotify_put_mark(iter_info->inode_mark);
-       }
-       if (iter_info->vfsmount_mark) {
-               group = iter_info->vfsmount_mark->group;
-               fsnotify_put_mark(iter_info->vfsmount_mark);
-       }
-       /*
-        * We abuse notification_waitq on group shutdown for waiting for all
-        * marks pinned when waiting for userspace.
-        */
-       if (atomic_dec_and_test(&group->user_waits) && group->shutdown)
-               wake_up(&group->notification_waitq);
+       fsnotify_put_mark_wake(iter_info->inode_mark);
+       fsnotify_put_mark_wake(iter_info->vfsmount_mark);
 }
 
 /*