|
@@ -105,18 +105,8 @@ static DECLARE_WORK(connector_reaper_work, fsnotify_connector_destroy_workfn);
|
|
|
|
|
|
void fsnotify_get_mark(struct fsnotify_mark *mark)
|
|
|
{
|
|
|
- WARN_ON_ONCE(!atomic_read(&mark->refcnt));
|
|
|
- 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);
|
|
|
+ WARN_ON_ONCE(!refcount_read(&mark->refcnt));
|
|
|
+ refcount_inc(&mark->refcnt);
|
|
|
}
|
|
|
|
|
|
static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
|
|
@@ -211,7 +201,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
|
|
|
|
|
|
/* Catch marks that were actually never attached to object */
|
|
|
if (!mark->connector) {
|
|
|
- if (atomic_dec_and_test(&mark->refcnt))
|
|
|
+ if (refcount_dec_and_test(&mark->refcnt))
|
|
|
fsnotify_final_mark_destroy(mark);
|
|
|
return;
|
|
|
}
|
|
@@ -220,7 +210,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
|
|
|
* We have to be careful so that traversals of obj_list under lock can
|
|
|
* safely grab mark reference.
|
|
|
*/
|
|
|
- if (!atomic_dec_and_lock(&mark->refcnt, &mark->connector->lock))
|
|
|
+ if (!refcount_dec_and_lock(&mark->refcnt, &mark->connector->lock))
|
|
|
return;
|
|
|
|
|
|
conn = mark->connector;
|
|
@@ -256,32 +246,60 @@ 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;
|
|
|
+
|
|
|
+ if (refcount_inc_not_zero(&mark->refcnt)) {
|
|
|
+ spin_lock(&mark->lock);
|
|
|
+ if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) {
|
|
|
+ /* mark is attached, group is still alive then */
|
|
|
+ atomic_inc(&mark->group->user_waits);
|
|
|
+ spin_unlock(&mark->lock);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ spin_unlock(&mark->lock);
|
|
|
+ fsnotify_put_mark(mark);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
|
|
|
- /*
|
|
|
- * 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);
|
|
|
+/*
|
|
|
+ * 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 +310,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);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -338,7 +335,7 @@ void fsnotify_detach_mark(struct fsnotify_mark *mark)
|
|
|
|
|
|
WARN_ON_ONCE(!mutex_is_locked(&group->mark_mutex));
|
|
|
WARN_ON_ONCE(!srcu_read_lock_held(&fsnotify_mark_srcu) &&
|
|
|
- atomic_read(&mark->refcnt) < 1 +
|
|
|
+ refcount_read(&mark->refcnt) < 1 +
|
|
|
!!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED));
|
|
|
|
|
|
spin_lock(&mark->lock);
|
|
@@ -599,9 +596,11 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct inode *inode,
|
|
|
|
|
|
return ret;
|
|
|
err:
|
|
|
+ spin_lock(&mark->lock);
|
|
|
mark->flags &= ~(FSNOTIFY_MARK_FLAG_ALIVE |
|
|
|
FSNOTIFY_MARK_FLAG_ATTACHED);
|
|
|
list_del_init(&mark->g_list);
|
|
|
+ spin_unlock(&mark->lock);
|
|
|
atomic_dec(&group->num_marks);
|
|
|
|
|
|
fsnotify_put_mark(mark);
|
|
@@ -738,7 +737,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark,
|
|
|
{
|
|
|
memset(mark, 0, sizeof(*mark));
|
|
|
spin_lock_init(&mark->lock);
|
|
|
- atomic_set(&mark->refcnt, 1);
|
|
|
+ refcount_set(&mark->refcnt, 1);
|
|
|
fsnotify_get_group(group);
|
|
|
mark->group = group;
|
|
|
}
|