summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis Henriques <luis@igalia.com>2025-09-16 14:53:09 +0100
committerMiklos Szeredi <mszeredi@redhat.com>2025-11-12 11:45:03 +0100
commit64becd224ff99dbdcffab22709dfcf170e52aff1 (patch)
tree2beea0c596c49d515aa14fcf585c357b59320f4b
parentab84ad5973869a660ca3ad0c54a2b84d975d47c4 (diff)
fuse: new work queue to invalidate dentries from old epochs
With the infrastructure introduced to periodically invalidate expired dentries, it is now possible to add an extra work queue to invalidate dentries when an epoch is incremented. This work queue will only be triggered when the 'inval_wq' parameter is set. Signed-off-by: Luis Henriques <luis@igalia.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/dev.c7
-rw-r--r--fs/fuse/dir.c21
-rw-r--r--fs/fuse/fuse_i.h4
-rw-r--r--fs/fuse/inode.c2
4 files changed, 31 insertions, 3 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 49b18d7accb3..6d59cbc877c6 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2041,13 +2041,14 @@ static int fuse_notify_resend(struct fuse_conn *fc)
/*
* Increments the fuse connection epoch. This will result of dentries from
- * previous epochs to be invalidated.
- *
- * XXX optimization: add call to shrink_dcache_sb()?
+ * previous epochs to be invalidated. Additionally, if inval_wq is set, a work
+ * queue is scheduled to trigger the invalidation.
*/
static int fuse_notify_inc_epoch(struct fuse_conn *fc)
{
atomic_inc(&fc->epoch);
+ if (inval_wq)
+ schedule_work(&fc->epoch_work);
return 0;
}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 77982fdbcf27..8ef8134e1cd5 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -189,6 +189,27 @@ static void fuse_dentry_tree_work(struct work_struct *work)
secs_to_jiffies(inval_wq));
}
+void fuse_epoch_work(struct work_struct *work)
+{
+ struct fuse_conn *fc = container_of(work, struct fuse_conn,
+ epoch_work);
+ struct fuse_mount *fm;
+ struct inode *inode;
+
+ down_read(&fc->killsb);
+
+ inode = fuse_ilookup(fc, FUSE_ROOT_ID, &fm);
+ iput(inode);
+
+ if (fm) {
+ /* Remove all possible active references to cached inodes */
+ shrink_dcache_sb(fm->sb);
+ } else
+ pr_warn("Failed to get root inode");
+
+ up_read(&fc->killsb);
+}
+
void fuse_dentry_tree_init(void)
{
int i;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index ac717b3b46a1..a80411028254 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -649,6 +649,8 @@ struct fuse_conn {
/** Current epoch for up-to-date dentries */
atomic_t epoch;
+ struct work_struct epoch_work;
+
struct rcu_head rcu;
/** The user id for this mount */
@@ -1287,6 +1289,8 @@ void fuse_check_timeout(struct work_struct *work);
void fuse_dentry_tree_init(void);
void fuse_dentry_tree_cleanup(void);
+void fuse_epoch_work(struct work_struct *work);
+
/**
* Invalidate inode attributes
*/
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 793f1766ae5a..3087165a6004 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -977,6 +977,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
refcount_set(&fc->count, 1);
atomic_set(&fc->dev_count, 1);
atomic_set(&fc->epoch, 1);
+ INIT_WORK(&fc->epoch_work, fuse_epoch_work);
init_waitqueue_head(&fc->blocked_waitq);
fuse_iqueue_init(&fc->iq, fiq_ops, fiq_priv);
INIT_LIST_HEAD(&fc->bg_queue);
@@ -1029,6 +1030,7 @@ void fuse_conn_put(struct fuse_conn *fc)
fuse_dax_conn_free(fc);
if (fc->timeout.req_timeout)
cancel_delayed_work_sync(&fc->timeout.work);
+ cancel_work_sync(&fc->epoch_work);
if (fiq->ops->release)
fiq->ops->release(fiq);
put_pid_ns(fc->pid_ns);