summaryrefslogtreecommitdiff
path: root/fs/notify
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2024-03-17 21:41:54 +0300
committerJan Kara <jack@suse.cz>2024-04-04 17:24:16 +0300
commita5e57b4d370c6d320e5bfb0c919fe00aee29e039 (patch)
tree846c1566071186acee107432ed2d18117872a3c9 /fs/notify
parent477cf917dd02853ba78a73cdeb6548889e5f8cd7 (diff)
downloadlinux-a5e57b4d370c6d320e5bfb0c919fe00aee29e039.tar.xz
fsnotify: optimize the case of no permission event watchers
Commit e43de7f0862b ("fsnotify: optimize the case of no marks of any type") optimized the case where there are no fsnotify watchers on any of the filesystem's objects. It is quite common for a system to have a single local filesystem and it is quite common for the system to have some inotify watches on some config files or directories, so the optimization of no marks at all is often not in effect. Permission event watchers, which require high priority group are more rare, so optimizing the case of no marks og high priority groups can improve performance for more systems, especially for performance sensitive io workloads. Count per-sb watched objects by high priority groups and use that the optimize out the call to __fsnotify_parent() and fsnotify() in fsnotify permission hooks. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz> Message-Id: <20240317184154.1200192-11-amir73il@gmail.com>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fsnotify.c3
-rw-r--r--fs/notify/mark.c30
2 files changed, 30 insertions, 3 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index fb3f36bc6ea9..2ae965ef37e8 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -100,6 +100,9 @@ void fsnotify_sb_delete(struct super_block *sb)
/* Wait for outstanding object references from connectors */
wait_var_event(fsnotify_sb_watched_objects(sb),
!atomic_long_read(fsnotify_sb_watched_objects(sb)));
+ WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
+ WARN_ON(fsnotify_sb_has_priority_watchers(sb,
+ FSNOTIFY_PRIO_PRE_CONTENT));
kfree(sbinfo);
}
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index b2f5d8c9cce1..c3eefa70633c 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -161,13 +161,36 @@ static void fsnotify_put_inode_ref(struct inode *inode)
static void fsnotify_update_sb_watchers(struct super_block *sb,
struct fsnotify_mark_connector *conn)
{
+ struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
bool is_watched = conn->flags & FSNOTIFY_CONN_FLAG_IS_WATCHED;
- bool has_marks = conn->obj && !hlist_empty(&conn->list);
+ struct fsnotify_mark *first_mark = NULL;
+ unsigned int highest_prio = 0;
- if (has_marks && !is_watched) {
+ if (conn->obj)
+ first_mark = hlist_entry_safe(conn->list.first,
+ struct fsnotify_mark, obj_list);
+ if (first_mark)
+ highest_prio = first_mark->group->priority;
+ if (WARN_ON(highest_prio >= __FSNOTIFY_PRIO_NUM))
+ highest_prio = 0;
+
+ /*
+ * If the highest priority of group watching this object is prio,
+ * then watched object has a reference on counters [0..prio].
+ * Update priority >= 1 watched objects counters.
+ */
+ for (unsigned int p = conn->prio + 1; p <= highest_prio; p++)
+ atomic_long_inc(&sbinfo->watched_objects[p]);
+ for (unsigned int p = conn->prio; p > highest_prio; p--)
+ atomic_long_dec(&sbinfo->watched_objects[p]);
+ conn->prio = highest_prio;
+
+ /* Update priority >= 0 (a.k.a total) watched objects counter */
+ BUILD_BUG_ON(FSNOTIFY_PRIO_NORMAL != 0);
+ if (first_mark && !is_watched) {
conn->flags |= FSNOTIFY_CONN_FLAG_IS_WATCHED;
fsnotify_get_sb_watched_objects(sb);
- } else if (!has_marks && is_watched) {
+ } else if (!first_mark && is_watched) {
conn->flags &= ~FSNOTIFY_CONN_FLAG_IS_WATCHED;
fsnotify_put_sb_watched_objects(sb);
}
@@ -600,6 +623,7 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
spin_lock_init(&conn->lock);
INIT_HLIST_HEAD(&conn->list);
conn->flags = 0;
+ conn->prio = 0;
conn->type = obj_type;
conn->obj = obj;