Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 24c20305 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Jan Kara
Browse files

fsnotify: clean up fsnotify_prepare/finish_user_wait()



This patch doesn't actually fix any bug, just paves the way for fixing mark
and group pinning.

Reviewed-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Cc: <stable@vger.kernel.org> # v4.12
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 7761daa6
Loading
Loading
Loading
Loading
+48 −48
Original line number Diff line number Diff line
@@ -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 (iter_info->inode_mark) {
	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;

		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);
	}
}

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))
			goto out_wait;
	}
	if (iter_info->vfsmount_mark) {
		if (!fsnotify_get_mark_safe(iter_info->vfsmount_mark))
			goto out_inode;
		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);
}

/*