All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jan Kara <jack@suse.cz>
To: <linux-fsdevel@vger.kernel.org>
Cc: Miklos Szeredi <miklos@szeredi.hu>,
	Amir Goldstein <amir73il@gmail.com>,
	Paul Moore <paul@paul-moore.com>, Jan Kara <jack@suse.cz>
Subject: [PATCH 18/35] fsnotify: Lock object list with connector lock
Date: Mon,  3 Apr 2017 17:34:07 +0200	[thread overview]
Message-ID: <20170403153424.24945-19-jack@suse.cz> (raw)
In-Reply-To: <20170403153424.24945-1-jack@suse.cz>

So far list of marks attached to an object (inode / vfsmount) was
protected by i_lock or mnt_root->d_lock. This dictates that the list
must be empty before the object can be destroyed although the list is
now anchored in the fsnotify_mark_connector structure. Protect the list
by a spinlock in the fsnotify_mark_connector structure to decouple
lifetime of a list of marks from a lifetime of the object. This also
simplifies the code quite a bit since we don't have to differentiate
between inode and vfsmount lists in quite a few places anymore.

Reviewed-by: Miklos Szeredi <mszeredi@redhat.com>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/notify/mark.c                 | 90 ++++++++++++++--------------------------
 include/linux/fsnotify_backend.h |  3 +-
 2 files changed, 34 insertions(+), 59 deletions(-)

diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index b5b641a2b557..bfb415d0d757 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -33,7 +33,7 @@
  *
  * group->mark_mutex
  * mark->lock
- * inode->i_lock
+ * mark->connector->lock
  *
  * group->mark_mutex protects the marks_list anchored inside a given group and
  * each mark is hooked via the g_list.  It also protects the groups private
@@ -44,10 +44,12 @@
  * is assigned to as well as the access to a reference of the inode/vfsmount
  * that is being watched by the mark.
  *
- * inode->i_lock protects the i_fsnotify_marks list anchored inside a
- * given inode and each mark is hooked via the i_list. (and sorta the
- * free_i_list)
+ * mark->connector->lock protects the list of marks anchored inside an
+ * inode / vfsmount and each mark is hooked via the i_list.
  *
+ * A list of notification marks relating to inode / mnt is contained in
+ * fsnotify_mark_connector. That structure is alive as long as there are any
+ * marks in the list and is also protected by fsnotify_mark_srcu.
  *
  * LIFETIME:
  * Inode marks survive between when they are added to an inode and when their
@@ -110,8 +112,10 @@ static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
 	u32 new_mask = 0;
 	struct fsnotify_mark *mark;
 
+	assert_spin_locked(&conn->lock);
 	hlist_for_each_entry(mark, &conn->list, obj_list)
 		new_mask |= mark->mask;
+
 	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
 		conn->inode->i_fsnotify_mask = new_mask;
 	else if (conn->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT)
@@ -128,31 +132,20 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
 	if (!conn)
 		return;
 
-	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
-		spin_lock(&conn->inode->i_lock);
-	else
-		spin_lock(&conn->mnt->mnt_root->d_lock);
+	spin_lock(&conn->lock);
 	__fsnotify_recalc_mask(conn);
-	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE) {
-		spin_unlock(&conn->inode->i_lock);
+	spin_unlock(&conn->lock);
+	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
 		__fsnotify_update_child_dentry_flags(conn->inode);
-	} else {
-		spin_unlock(&conn->mnt->mnt_root->d_lock);
-	}
 }
 
 static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
 {
 	struct fsnotify_mark_connector *conn;
 	struct inode *inode = NULL;
-	spinlock_t *lock;
 
 	conn = mark->connector;
-	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
-		lock = &conn->inode->i_lock;
-	else
-		lock = &conn->mnt->mnt_root->d_lock;
-	spin_lock(lock);
+	spin_lock(&conn->lock);
 	hlist_del_init_rcu(&mark->obj_list);
 	if (hlist_empty(&conn->list)) {
 		if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
@@ -160,7 +153,7 @@ static struct inode *fsnotify_detach_from_object(struct fsnotify_mark *mark)
 	}
 	__fsnotify_recalc_mask(conn);
 	mark->connector = NULL;
-	spin_unlock(lock);
+	spin_unlock(&conn->lock);
 
 	return inode;
 }
@@ -326,7 +319,6 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
 
 static int fsnotify_attach_connector_to_object(
 					struct fsnotify_mark_connector **connp,
-					spinlock_t *lock,
 					struct inode *inode,
 					struct vfsmount *mnt)
 {
@@ -335,6 +327,7 @@ static int fsnotify_attach_connector_to_object(
 	conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
 	if (!conn)
 		return -ENOMEM;
+	spin_lock_init(&conn->lock);
 	INIT_HLIST_HEAD(&conn->list);
 	if (inode) {
 		conn->flags = FSNOTIFY_OBJ_TYPE_INODE;
@@ -344,16 +337,13 @@ static int fsnotify_attach_connector_to_object(
 		conn->mnt = mnt;
 	}
 	/*
-	 * Make sure 'conn' initialization is visible. Matches
-	 * lockless_dereference() in fsnotify().
+	 * cmpxchg() provides the barrier so that readers of *connp can see
+	 * only initialized structure
 	 */
-	smp_wmb();
-	spin_lock(lock);
-	if (!*connp)
-		*connp = conn;
-	else
+	if (cmpxchg(connp, NULL, conn)) {
+		/* Someone else created list structure for us */
 		kmem_cache_free(fsnotify_mark_connector_cachep, conn);
-	spin_unlock(lock);
+	}
 
 	return 0;
 }
@@ -371,35 +361,30 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
 	struct fsnotify_mark *lmark, *last = NULL;
 	struct fsnotify_mark_connector *conn;
 	struct fsnotify_mark_connector **connp;
-	spinlock_t *lock;
 	int cmp;
 	int err = 0;
 
 	if (WARN_ON(!inode && !mnt))
 		return -EINVAL;
-	if (inode) {
+	if (inode)
 		connp = &inode->i_fsnotify_marks;
-		lock = &inode->i_lock;
-	} else {
+	else
 		connp = &real_mount(mnt)->mnt_fsnotify_marks;
-		lock = &mnt->mnt_root->d_lock;
-	}
 
 	if (!*connp) {
-		err = fsnotify_attach_connector_to_object(connp, lock,
-							  inode, mnt);
+		err = fsnotify_attach_connector_to_object(connp, inode, mnt);
 		if (err)
 			return err;
 	}
 	spin_lock(&mark->lock);
-	spin_lock(lock);
 	conn = *connp;
+	spin_lock(&conn->lock);
 
 	/* is mark the first mark? */
 	if (hlist_empty(&conn->list)) {
 		hlist_add_head_rcu(&mark->obj_list, &conn->list);
 		if (inode)
-			__iget(inode);
+			igrab(inode);
 		goto added;
 	}
 
@@ -425,7 +410,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
 added:
 	mark->connector = conn;
 out_err:
-	spin_unlock(lock);
+	spin_unlock(&conn->lock);
 	spin_unlock(&mark->lock);
 	return err;
 }
@@ -449,7 +434,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
 	 * LOCKING ORDER!!!!
 	 * group->mark_mutex
 	 * mark->lock
-	 * inode->i_lock
+	 * mark->connector->lock
 	 */
 	spin_lock(&mark->lock);
 	mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_ATTACHED;
@@ -505,24 +490,19 @@ struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_mark_connector *conn,
 					 struct fsnotify_group *group)
 {
 	struct fsnotify_mark *mark;
-	spinlock_t *lock;
 
 	if (!conn)
 		return NULL;
 
-	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
-		lock = &conn->inode->i_lock;
-	else
-		lock = &conn->mnt->mnt_root->d_lock;
-	spin_lock(lock);
+	spin_lock(&conn->lock);
 	hlist_for_each_entry(mark, &conn->list, obj_list) {
 		if (mark->group == group) {
 			fsnotify_get_mark(mark);
-			spin_unlock(lock);
+			spin_unlock(&conn->lock);
 			return mark;
 		}
 	}
-	spin_unlock(lock);
+	spin_unlock(&conn->lock);
 	return NULL;
 }
 
@@ -595,16 +575,10 @@ void fsnotify_detach_group_marks(struct fsnotify_group *group)
 void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn)
 {
 	struct fsnotify_mark *mark;
-	spinlock_t *lock;
 
 	if (!conn)
 		return;
 
-	if (conn->flags & FSNOTIFY_OBJ_TYPE_INODE)
-		lock = &conn->inode->i_lock;
-	else
-		lock = &conn->mnt->mnt_root->d_lock;
-
 	while (1) {
 		/*
 		 * We have to be careful since we can race with e.g.
@@ -613,15 +587,15 @@ void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn)
 		 * we are holding mark reference so mark cannot be freed and
 		 * calling fsnotify_destroy_mark() more than once is fine.
 		 */
-		spin_lock(lock);
+		spin_lock(&conn->lock);
 		if (hlist_empty(&conn->list)) {
-			spin_unlock(lock);
+			spin_unlock(&conn->lock);
 			break;
 		}
 		mark = hlist_entry(conn->list.first, struct fsnotify_mark,
 				   obj_list);
 		fsnotify_get_mark(mark);
-		spin_unlock(lock);
+		spin_unlock(&conn->lock);
 		fsnotify_destroy_mark(mark, mark->group);
 		fsnotify_put_mark(mark);
 	}
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index b954f1b2571c..02c6fac652a4 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -201,6 +201,7 @@ struct fsnotify_group {
  * inode / vfsmount gets freed.
  */
 struct fsnotify_mark_connector {
+	spinlock_t lock;
 #define FSNOTIFY_OBJ_TYPE_INODE		0x01
 #define FSNOTIFY_OBJ_TYPE_VFSMOUNT	0x02
 	unsigned int flags;	/* Type of object [lock] */
@@ -240,7 +241,7 @@ struct fsnotify_mark {
 	struct list_head g_list;
 	/* Protects inode / mnt pointers, flags, masks */
 	spinlock_t lock;
-	/* List of marks for inode / vfsmount [obj_lock] */
+	/* List of marks for inode / vfsmount [connector->lock] */
 	struct hlist_node obj_list;
 	/* Head of list of marks for an object [mark->lock, group->mark_mutex] */
 	struct fsnotify_mark_connector *connector;
-- 
2.10.2

  parent reply	other threads:[~2017-04-03 15:34 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-03 15:33 [PATCH 0/35 v7] fsnotify: Avoid SRCU stalls with fanotify permission events Jan Kara
2017-04-03 15:33 ` [PATCH 01/35] fsnotify: Remove unnecessary tests when showing fdinfo Jan Kara
2017-04-03 15:33 ` [PATCH 02/35] inotify: Remove inode pointers from debug messages Jan Kara
2017-04-03 15:33 ` [PATCH 03/35] fanotify: Move recalculation of inode / vfsmount mask under mark_mutex Jan Kara
2017-04-03 15:33 ` [PATCH 04/35] audit: Abstract hash key handling Jan Kara
2017-04-04 20:38   ` Paul Moore
2017-04-03 15:33 ` [PATCH 05/35] audit_tree: Use mark flags to check whether mark is alive Jan Kara
2017-04-04 20:47   ` Paul Moore
2017-04-05  7:38     ` Jan Kara
2017-04-06 11:51       ` Paul Moore
2017-04-10 15:31         ` Jan Kara
2017-04-10 15:43           ` Jan Kara
2017-04-11 20:27             ` Paul Moore
2017-04-03 15:33 ` [PATCH 06/35] fsnotify: Update comments Jan Kara
2017-04-03 15:33 ` [PATCH 07/35] fsnotify: Move mark list head from object into dedicated structure Jan Kara
2017-04-03 15:33 ` [PATCH 08/35] fsnotify: Move object pointer to fsnotify_mark_connector Jan Kara
2017-04-03 15:33 ` [PATCH 09/35] fsnotify: Make fsnotify_mark_connector hold inode reference Jan Kara
2017-04-03 15:33 ` [PATCH 10/35] fsnotify: Remove indirection from mark list addition Jan Kara
2017-04-03 15:34 ` [PATCH 11/35] fsnotify: Move fsnotify_destroy_marks() Jan Kara
2017-04-03 15:34 ` [PATCH 12/35] fsnotify: Move locking into fsnotify_recalc_mask() Jan Kara
2017-04-03 15:34 ` [PATCH 13/35] fsnotify: Move locking into fsnotify_find_mark() Jan Kara
2017-04-03 15:34 ` [PATCH 14/35] fsnotify: Determine lock in fsnotify_destroy_marks() Jan Kara
2017-04-03 15:34 ` [PATCH 15/35] fsnotify: Remove indirection from fsnotify_detach_mark() Jan Kara
2017-04-03 15:34 ` [PATCH 16/35] fsnotify: Avoid double locking in fsnotify_detach_from_object() Jan Kara
2017-04-03 15:34 ` [PATCH 17/35] fsnotify: Remove useless list deletion and comment Jan Kara
2017-04-03 15:34 ` Jan Kara [this message]
2017-04-03 15:34 ` [PATCH 19/35] fsnotify: Free fsnotify_mark_connector when there is no mark attached Jan Kara
2017-04-03 15:34 ` [PATCH 20/35] inotify: Do not drop mark reference under idr_lock Jan Kara
2017-04-03 15:34 ` [PATCH 21/35] fsnotify: Move queueing of mark for destruction into fsnotify_put_mark() Jan Kara
2017-04-03 15:34 ` [PATCH 22/35] fsnotify: Detach mark from object list when last reference is dropped Jan Kara
2017-04-03 15:34 ` [PATCH 23/35] fsnotify: Remove special handling of mark destruction on group shutdown Jan Kara
2017-04-03 15:34 ` [PATCH 24/35] fsnotify: Provide framework for dropping SRCU lock in ->handle_event Jan Kara
2017-04-03 15:34 ` [PATCH 25/35] fsnotify: Pass fsnotify_iter_info into handle_event handler Jan Kara
2017-04-03 15:34 ` [PATCH 26/35] fanotify: Release SRCU lock when waiting for userspace response Jan Kara
2017-04-03 15:34 ` [PATCH 27/35] fsnotify: Remove fsnotify_set_mark_{,ignored_}mask_locked() Jan Kara
2017-04-03 15:34 ` [PATCH 28/35] fsnotify: Remove fsnotify_recalc_{inode|vfsmount}_mask() Jan Kara
2017-04-03 15:34 ` [PATCH 29/35] fsnotify: Inline fsnotify_clear_{inode|vfsmount}_mark_group() Jan Kara
2017-04-03 15:34 ` [PATCH 30/35] fsnotify: Rename fsnotify_clear_marks_by_group_flags() Jan Kara
2017-04-03 15:34 ` [PATCH 31/35] fsnotify: Remove fsnotify_detach_group_marks() Jan Kara
2017-04-03 15:34 ` [PATCH 32/35] fsnotify: Remove fsnotify_find_{inode|vfsmount}_mark() Jan Kara
2017-04-03 15:34 ` [PATCH 33/35] fsnotify: Drop inode_mark.c Jan Kara
2017-04-03 15:34 ` [PATCH 34/35] fsnotify: Add group pointer in fsnotify_init_mark() Jan Kara
2017-04-03 15:34 ` [PATCH 35/35] fsnotify: Move ->free_mark callback to fsnotify_ops Jan Kara

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=20170403153424.24945-19-jack@suse.cz \
    --to=jack@suse.cz \
    --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.