@@ -153,20 +153,23 @@ static void fsnotify_put_inode_ref(struct inode *inode)
fsnotify_put_sb_watched_objects(inode->i_sb);
}
-static void fsnotify_get_sb_watchers(struct fsnotify_mark_connector *conn)
+/*
+ * Grab or drop watched objects reference depending on whether the connector
+ * is attached and has any marks attached.
+ */
+static void fsnotify_update_sb_watchers(struct super_block *sb,
+ struct fsnotify_mark_connector *conn)
{
- struct super_block *sb = fsnotify_connector_sb(conn);
+ bool is_watched = conn->flags & FSNOTIFY_CONN_FLAG_IS_WATCHED;
+ bool has_marks = conn->obj && !hlist_empty(&conn->list);
- if (sb)
+ if (has_marks && !is_watched) {
+ conn->flags |= FSNOTIFY_CONN_FLAG_IS_WATCHED;
fsnotify_get_sb_watched_objects(sb);
-}
-
-static void fsnotify_put_sb_watchers(struct fsnotify_mark_connector *conn)
-{
- struct super_block *sb = fsnotify_connector_sb(conn);
-
- if (sb)
+ } else if (!has_marks && is_watched) {
+ conn->flags &= ~FSNOTIFY_CONN_FLAG_IS_WATCHED;
fsnotify_put_sb_watched_objects(sb);
+ }
}
/*
@@ -265,6 +268,7 @@ static void *fsnotify_detach_connector_from_object(
unsigned int *type)
{
fsnotify_connp_t *connp = fsnotify_object_connp(conn->obj, conn->type);
+ struct super_block *sb = fsnotify_connector_sb(conn);
struct inode *inode = NULL;
*type = conn->type;
@@ -284,10 +288,10 @@ static void *fsnotify_detach_connector_from_object(
fsnotify_conn_sb(conn)->s_fsnotify_mask = 0;
}
- fsnotify_put_sb_watchers(conn);
rcu_assign_pointer(*connp, NULL);
conn->obj = NULL;
conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
+ fsnotify_update_sb_watchers(sb, conn);
return inode;
}
@@ -339,6 +343,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
objp = fsnotify_detach_connector_from_object(conn, &type);
free_conn = true;
} else {
+ struct super_block *sb = fsnotify_connector_sb(conn);
+
+ /* Update watched objects after detaching mark */
+ if (sb)
+ fsnotify_update_sb_watchers(sb, conn);
objp = __fsnotify_recalc_mask(conn);
type = conn->type;
}
@@ -580,10 +589,7 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
if (cmpxchg(connp, NULL, conn)) {
/* Someone else created list structure for us */
kmem_cache_free(fsnotify_mark_connector_cachep, conn);
- return 0;
}
-
- fsnotify_get_sb_watchers(conn);
return 0;
}
@@ -623,6 +629,7 @@ static struct fsnotify_mark_connector *fsnotify_grab_connector(
static int fsnotify_add_mark_list(struct fsnotify_mark *mark, void *obj,
unsigned int obj_type, int add_flags)
{
+ struct super_block *sb = fsnotify_object_sb(obj, obj_type);
struct fsnotify_mark *lmark, *last = NULL;
struct fsnotify_mark_connector *conn;
fsnotify_connp_t *connp;
@@ -672,6 +679,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark, void *obj,
/* mark should be the last entry. last is the current last entry */
hlist_add_behind_rcu(&mark->obj_list, &last->obj_list);
added:
+ fsnotify_update_sb_watchers(sb, conn);
/*
* Since connector is attached to object using cmpxchg() we are
* guaranteed that connector initialization is fully visible by anyone
@@ -465,6 +465,7 @@ FSNOTIFY_ITER_FUNCS(sb, SB)
struct fsnotify_mark_connector {
spinlock_t lock;
unsigned short type; /* Type of object [lock] */
+#define FSNOTIFY_CONN_FLAG_IS_WATCHED 0x01
#define FSNOTIFY_CONN_FLAG_HAS_IREF 0x02
unsigned short flags; /* flags [lock] */
union {
We would like to count watched objects by priority group, so we will need to update the watched object counter after adding/removing marks. Create a helper fsnotify_update_sb_watchers() and call it after attaching/detaching a mark, instead of fsnotify_{get,put}_sb_watchers() only after attaching/detaching a connector. Soon, we will use this helper to count watched objects by the highest watching priority group. Signed-off-by: Amir Goldstein <amir73il@gmail.com> --- fs/notify/mark.c | 36 +++++++++++++++++++------------- include/linux/fsnotify_backend.h | 1 + 2 files changed, 23 insertions(+), 14 deletions(-)