From: Jan Kara <jack@suse.cz>
To: <linux-fsdevel@vger.kernel.org>
Cc: Amir Goldstein <amir73il@gmail.com>,
Lino Sanfilippo <LinoSanfilippo@gmx.de>,
Miklos Szeredi <miklos@szeredi.hu>,
Paul Moore <paul@paul-moore.com>, Jan Kara <jack@suse.cz>
Subject: [PATCH 10/22] fsnotify: Detach mark from object list when last reference is dropped
Date: Thu, 22 Dec 2016 10:15:26 +0100 [thread overview]
Message-ID: <20161222091538.28702-11-jack@suse.cz> (raw)
In-Reply-To: <20161222091538.28702-1-jack@suse.cz>
Instead of removing mark from object list from fsnotify_detach_mark(),
remove the mark when last reference to the mark is dropped. This will
allow fanotify to wait for userspace response to event without having to
hold onto fsnotify_mark_srcu.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/notify/mark.c | 166 +++++++++++++++++++++++++--------------
include/linux/fsnotify_backend.h | 4 +-
2 files changed, 108 insertions(+), 62 deletions(-)
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index a9870beabe67..55550dad6617 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -49,7 +49,13 @@
*
* A list of notification marks relating to inode / mnt is contained in
* fsnotify_mark_list. That structure is alive as long as there are any marks
- * in the list and is also protected by fsnotify_mark_srcu.
+ * in the list and is also protected by fsnotify_mark_srcu. A mark gets
+ * detached from fsnotify_mark_list when last reference to the mark is dropped.
+ * Thus having mark reference is enough to protect mark->obj_list_head pointer
+ * and to make sure fsnotify_mark_list cannot disappear. Also because we remove
+ * mark from g_list before dropping mark reference associated with that, any
+ * mark found through g_list is guaranteed to have mark->obj_list_head set
+ * until we drop group->mark_mutex.
*
* LIFETIME:
* Inode marks survive between when they are added to an inode and when their
@@ -102,15 +108,6 @@ void fsnotify_get_mark(struct fsnotify_mark *mark)
atomic_inc(&mark->refcnt);
}
-void fsnotify_put_mark(struct fsnotify_mark *mark)
-{
- if (atomic_dec_and_test(&mark->refcnt)) {
- if (mark->group)
- fsnotify_put_group(mark->group);
- mark->free_mark(mark);
- }
-}
-
static void __fsnotify_recalc_mask(struct fsnotify_mark_list *list)
{
u32 new_mask = 0;
@@ -165,34 +162,47 @@ static void fsnotify_list_destroy_workfn(struct work_struct *work)
}
}
-static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
+static struct inode *fsnotify_detach_list_from_object(
+ struct fsnotify_mark_list *list)
+{
+ struct inode *inode = NULL;
+
+ if (list->flags & FSNOTIFY_LIST_TYPE_INODE) {
+ inode = list->inode;
+ inode->i_fsnotify_marks = NULL;
+ inode->i_fsnotify_mask = 0;
+ list->inode = NULL;
+ list->flags &= ~FSNOTIFY_LIST_TYPE_INODE;
+ } else if (list->flags & FSNOTIFY_LIST_TYPE_VFSMOUNT) {
+ real_mount(list->mnt)->mnt_fsnotify_marks = NULL;
+ real_mount(list->mnt)->mnt_fsnotify_mask = 0;
+ list->mnt = NULL;
+ list->flags &= ~FSNOTIFY_LIST_TYPE_VFSMOUNT;
+ }
+
+ return inode;
+}
+
+/* Called with mark->obj_list_head->lock held, releases it */
+static void fsnotify_detach_from_object(struct fsnotify_mark *mark)
{
struct fsnotify_mark_list *list;
struct inode *inode = NULL;
bool free_list = false;
list = mark->obj_list_head;
- spin_lock(&list->lock);
hlist_del_init_rcu(&mark->obj_list);
if (hlist_empty(&list->list)) {
- if (list->flags & FSNOTIFY_LIST_TYPE_INODE) {
- inode = list->inode;
- inode->i_fsnotify_marks = NULL;
- inode->i_fsnotify_mask = 0;
- list->inode = NULL;
- list->flags &= ~FSNOTIFY_LIST_TYPE_INODE;
- } else if (list->flags & FSNOTIFY_LIST_TYPE_VFSMOUNT) {
- real_mount(list->mnt)->mnt_fsnotify_marks = NULL;
- real_mount(list->mnt)->mnt_fsnotify_mask = 0;
- list->mnt = NULL;
- list->flags &= ~FSNOTIFY_LIST_TYPE_VFSMOUNT;
- }
+ inode = fsnotify_detach_list_from_object(list);
free_list = true;
} else
__fsnotify_recalc_mask(list);
mark->obj_list_head = NULL;
spin_unlock(&list->lock);
+ if (inode)
+ iput(inode);
+
if (free_list) {
spin_lock(&destroy_lock);
list->destroy_next = list_destroy_list;
@@ -200,49 +210,66 @@ static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
spin_unlock(&destroy_lock);
queue_work(system_unbound_wq, &list_reaper_work);
}
+}
- return inode;
+static void fsnotify_final_mark_destroy(struct fsnotify_mark *mark)
+{
+ if (mark->group)
+ fsnotify_put_group(mark->group);
+ mark->free_mark(mark);
+}
+
+void fsnotify_put_mark(struct fsnotify_mark *mark)
+{
+ /* Catch marks that were actually never attached to object */
+ if (!mark->obj_list_head && atomic_dec_and_test(&mark->refcnt)) {
+ fsnotify_final_mark_destroy(mark);
+ return;
+ }
+
+ /*
+ * 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->obj_list_head->lock)) {
+ fsnotify_detach_from_object(mark);
+ /*
+ * Note that we didn't update flags telling whether inode cares
+ * about what's happening with children. We update these flags
+ * from __fsnotify_parent() lazily when next event happens on
+ * one of our children.
+ */
+ fsnotify_final_mark_destroy(mark);
+ }
}
/*
- * Remove mark from inode / vfsmount list, group list, drop inode reference
- * if we got one.
+ * Mark mark as dead, remove it from group list. Mark still stays in object
+ * list until its last reference is dropped. The reference corresponding to
+ * group list gets dropped after SRCU period ends from
+ * fsnotify_mark_destroy_list(). Note that we rely on mark being removed from
+ * group list before corresponding reference to it is dropped. In particular we
+ * rely on mark->obj_list_head being valid while we hold group->mark_mutex if
+ * we found the mark through g_list.
*
* Must be called with group->mark_mutex held.
*/
void fsnotify_detach_mark(struct fsnotify_mark *mark)
{
- struct inode *inode = NULL;
struct fsnotify_group *group = mark->group;
BUG_ON(!mutex_is_locked(&group->mark_mutex));
spin_lock(&mark->lock);
-
/* something else already called this function on this mark */
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
spin_unlock(&mark->lock);
return;
}
-
mark->flags &= ~FSNOTIFY_MARK_FLAG_ATTACHED;
-
- inode = fsnotify_detach_from_object(mark);
-
- /*
- * Note that we didn't update flags telling whether inode cares about
- * what's happening with children. We update these flags from
- * __fsnotify_parent() lazily when next event happens on one of our
- * children.
- */
-
list_del_init(&mark->g_list);
-
spin_unlock(&mark->lock);
- if (inode)
- iput(inode);
-
atomic_dec(&group->num_marks);
}
@@ -445,7 +472,9 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
hlist_for_each_entry(lmark, &list->list, obj_list) {
last = lmark;
- if ((lmark->group == mark->group) && !allow_dups) {
+ if ((lmark->group == mark->group) &&
+ (lmark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) &&
+ !allow_dups) {
err = -EEXIST;
goto out_err;
}
@@ -496,7 +525,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
mark->group = group;
list_add(&mark->g_list, &group->marks_list);
atomic_inc(&group->num_marks);
- fsnotify_get_mark(mark); /* for i_list and g_list */
+ fsnotify_get_mark(mark); /* for g_list */
spin_unlock(&mark->lock);
ret = fsnotify_add_mark_list(mark, inode, mnt, allow_dups);
@@ -549,7 +578,8 @@ struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_mark_list **listp,
if (!list)
return NULL;
hlist_for_each_entry(mark, &list->list, obj_list) {
- if (mark->group == group) {
+ if (mark->group == group &&
+ (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)) {
fsnotify_get_mark(mark);
fsnotify_put_list(list);
return mark;
@@ -629,23 +659,39 @@ void fsnotify_detach_group_marks(struct fsnotify_group *group)
void fsnotify_destroy_marks(struct fsnotify_mark_list **listp)
{
struct fsnotify_mark_list *list;
- struct fsnotify_mark *mark;
+ struct fsnotify_mark *mark, *old_mark = NULL;
+ struct inode *inode;
- while ((list = fsnotify_grab_list(listp))) {
- /*
- * We have to be careful since we can race with e.g.
- * fsnotify_clear_marks_by_group() and once we drop the list
- * lock, mark can get removed from the obj_list and destroyed.
- * But we are holding mark reference so mark cannot be freed
- * and calling fsnotify_destroy_mark() more than once is fine.
- */
- mark = hlist_entry(list->list.first, struct fsnotify_mark,
- obj_list);
+ list = fsnotify_grab_list(listp);
+ if (!list)
+ return;
+ /*
+ * We have to be careful since we can race with e.g.
+ * fsnotify_clear_marks_by_group() and once we drop the list->lock, the
+ * list can get modified. However we are holding mark reference and
+ * thus our mark cannot be removed from obj_list so we can continue
+ * iteration after regaining list->lock.
+ */
+ hlist_for_each_entry(mark, &list->list, obj_list) {
fsnotify_get_mark(mark);
- fsnotify_put_list(list);
+ spin_unlock(&list->lock);
+ if (old_mark)
+ fsnotify_put_mark(old_mark);
+ old_mark = mark;
fsnotify_destroy_mark(mark, mark->group);
- fsnotify_put_mark(mark);
+ spin_lock(&list->lock);
}
+ /*
+ * Detach list from object now so that we don't pin inode until all
+ * mark references get dropped. It would lead to strange results such
+ * as delaying inode deletion or blocking unmount.
+ */
+ inode = fsnotify_detach_list_from_object(list);
+ fsnotify_put_list(list);
+ if (inode)
+ iput(inode);
+ if (old_mark)
+ fsnotify_put_mark(old_mark);
}
/*
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 6086fc7ff6df..76b3c34172c7 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -244,9 +244,9 @@ struct fsnotify_mark {
struct list_head g_list;
/* Protects inode / mnt pointers, flags, masks */
spinlock_t lock;
- /* List of marks for inode / vfsmount [obj_list_head->lock] */
+ /* List of marks for inode / vfsmount [obj_list_head->lock, mark ref] */
struct hlist_node obj_list;
- /* Head of list of marks for an object [mark->lock, group->mark_mutex] */
+ /* Head of list of marks for an object [mark ref] */
struct fsnotify_mark_list *obj_list_head;
/* Events types to ignore [mark->lock, group->mark_mutex] */
__u32 ignored_mask;
--
2.10.2
next prev parent reply other threads:[~2016-12-22 9:15 UTC|newest]
Thread overview: 80+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-22 9:15 [PATCH 0/22] fsnotify: Avoid SRCU stalls with fanotify permission events Jan Kara
2016-12-22 9:15 ` [PATCH 01/22] fsnotify: Remove unnecessary tests when showing fdinfo Jan Kara
2016-12-22 12:59 ` Amir Goldstein
2016-12-22 15:16 ` Jan Kara
2016-12-22 15:54 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 02/22] inotify: Remove inode pointers from debug messages Jan Kara
2016-12-22 15:31 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 03/22] fanotify: Move recalculation of inode / vfsmount mask under mark_mutex Jan Kara
2016-12-22 16:27 ` Amir Goldstein
2016-12-22 17:31 ` Jan Kara
2016-12-22 19:08 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 04/22] fsnotify: Remove fsnotify_duplicate_mark() Jan Kara
2016-12-22 23:13 ` Paul Moore
2016-12-23 13:22 ` Jan Kara
2016-12-23 14:01 ` Paul Moore
2016-12-22 9:15 ` [PATCH 05/22] audit: Fix sleep in atomic Jan Kara
2016-12-22 23:18 ` Paul Moore
2016-12-23 13:24 ` Jan Kara
2016-12-23 14:17 ` Paul Moore
2016-12-26 16:33 ` Paul Moore
2017-01-02 18:21 ` Jan Kara
2017-01-03 21:11 ` Paul Moore
2017-01-03 21:11 ` Paul Moore
2017-01-04 8:50 ` Jan Kara
2017-01-05 2:14 ` Paul Moore
2016-12-22 9:15 ` [PATCH 06/22] audit: Abstract hash key handling Jan Kara
2016-12-22 23:27 ` Paul Moore
2016-12-23 13:27 ` Jan Kara
2016-12-23 14:13 ` Paul Moore
2017-01-03 17:34 ` Jan Kara
2017-01-05 2:06 ` Paul Moore
2016-12-22 9:15 ` [PATCH 07/22] fsnotify: Update comments Jan Kara
2016-12-23 4:45 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 08/22] fsnotify: Attach marks to object via dedicated head structure Jan Kara
2016-12-23 5:48 ` Amir Goldstein
2016-12-23 13:34 ` Jan Kara
2017-01-04 13:38 ` Jan Kara
2017-01-04 15:29 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 09/22] inotify: Do not drop mark reference under idr_lock Jan Kara
2016-12-23 8:04 ` Amir Goldstein
2016-12-22 9:15 ` Jan Kara [this message]
2016-12-23 10:51 ` [PATCH 10/22] fsnotify: Detach mark from object list when last reference is dropped Amir Goldstein
2016-12-23 13:42 ` Jan Kara
2016-12-22 9:15 ` [PATCH 11/22] fsnotify: Remove special handling of mark destruction on group shutdown Jan Kara
2016-12-23 12:12 ` Amir Goldstein
2016-12-23 13:31 ` Jan Kara
2016-12-22 9:15 ` [PATCH 12/22] fsnotify: Move queueing of mark for destruction into fsnotify_put_mark() Jan Kara
2016-12-26 14:15 ` Amir Goldstein
2017-01-04 10:28 ` Jan Kara
2017-01-04 12:22 ` Jan Kara
2016-12-22 9:15 ` [PATCH 13/22] fsnotify: Provide framework for dropping SRCU lock in ->handle_event Jan Kara
2016-12-26 15:01 ` Amir Goldstein
2016-12-26 15:11 ` Amir Goldstein
2017-01-04 9:03 ` Jan Kara
2017-01-04 10:50 ` Amir Goldstein
2017-01-04 11:45 ` Jan Kara
2016-12-26 18:37 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 14/22] fsnotify: Pass SRCU index into handle_event handler Jan Kara
2016-12-26 15:13 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 15/22] fanotify: Release SRCU lock when waiting for userspace response Jan Kara
2016-12-26 15:22 ` Amir Goldstein
2017-01-04 9:05 ` Jan Kara
2016-12-22 9:15 ` [PATCH 16/22] fsnotify: Remove fsnotify_set_mark_{,ignored_}mask_locked() Jan Kara
2016-12-26 16:42 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 17/22] fsnotify: Remove fsnotify_recalc_{inode|vfsmount}_mask() Jan Kara
2016-12-26 16:44 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 18/22] fsnotify: Inline fsnotify_clear_{inode|vfsmount|_mark_group() Jan Kara
2016-12-26 16:57 ` Amir Goldstein
2017-01-04 9:28 ` Jan Kara
2016-12-22 9:15 ` [PATCH 19/22] fsnotify: Remove fsnotify_find_{inode|vfsmount}_mark() Jan Kara
2016-12-26 17:14 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 20/22] fsnotify: Drop inode_mark.c Jan Kara
2016-12-26 17:15 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 21/22] fsnotify: Add group pointer in fsnotify_init_mark() Jan Kara
2016-12-26 17:34 ` Amir Goldstein
2016-12-22 9:15 ` [PATCH 22/22] fsnotify: Move ->free_mark callback to fsnotify_ops Jan Kara
2016-12-26 17:39 ` Amir Goldstein
2016-12-22 20:58 ` [PATCH 0/22] fsnotify: Avoid SRCU stalls with fanotify permission events Paul Moore
2016-12-22 21:05 ` Amir Goldstein
2016-12-22 23:04 ` Paul Moore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20161222091538.28702-11-jack@suse.cz \
--to=jack@suse.cz \
--cc=LinoSanfilippo@gmx.de \
--cc=amir73il@gmail.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=paul@paul-moore.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.