fsnotify: create helper fsnotify_update_sb_watchers()
authorAmir Goldstein <amir73il@gmail.com>
Sun, 17 Mar 2024 18:41:50 +0000 (20:41 +0200)
committerJan Kara <jack@suse.cz>
Thu, 4 Apr 2024 14:24:16 +0000 (16:24 +0200)
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>
Signed-off-by: Jan Kara <jack@suse.cz>
Message-Id: <20240317184154.1200192-7-amir73il@gmail.com>

fs/notify/mark.c
include/linux/fsnotify_backend.h

index 1f611e4eab53fe06b32a97df28139da6aaf3551a..647c49c2046739d988672da3068538b5b220aa6b 100644 (file)
@@ -154,20 +154,23 @@ static void fsnotify_put_inode_ref(struct inode *inode)
        iput(inode);
 }
 
-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);
+       }
 }
 
 /*
@@ -266,6 +269,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;
@@ -285,10 +289,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;
 }
@@ -340,6 +344,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;
        }
@@ -581,10 +590,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;
 }
 
@@ -624,6 +630,7 @@ out:
 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;
@@ -673,6 +680,7 @@ restart:
        /* 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
index face68fcf850793571eb2034b36fb7b711af2ac8..83004d9e07a31289b286cc2ba37656c2439c193d 100644 (file)
@@ -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 {