From nobody Thu Oct 2 13:03:49 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 194E831DDB6; Tue, 16 Sep 2025 13:53:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030814; cv=none; b=HUdpbgtx5LUu+BKPUEfVcq+wuVpRf+PNML1w6u7Ymbu4s9nKIM6gmqh0nKOSaAYBxPGLUI4ifVTe2L49E1umPlhLBSPgqu3zCoSKQfljlYW4jxludy6rZfXI14/d2Tf46hblsT5bhMZ7jcgB0xWsW7+9ZF+DLvVKMZFORkkJZz0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030814; c=relaxed/simple; bh=SMGSmx2pqz5voqtJR3czA7f6GuqUU80n5EtyBj5q9GM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ueQH+TLPshvKeXNPjog6AJFuvmlHrqVl2WT95nsTKE7a0SxDTMSc/vpKp1viNktnX24aFWsS4daXwJatwNYFrw8OacDLL6sNy7xlCfuW1CXAr014JkOdDet7Gm17ExrgEzWST1luMTYR/DKR0hPUTBNI1F6RtI85oIYu3axKLi4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=r9JqvVPe; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="r9JqvVPe" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=UQ81DTt4EOw7u3fHJtPSWfenc7ZBjC0pxzgeRhwqK8s=; b=r9JqvVPelk/d/5P9WhRXm+04BY gJZRiyTQ7xxLDnaqW5VerHOoJmegcYCuv0dZcsBJPy1oIPozt8nUPFMbN/SI3VuZrO4+LYTgmsyU4 8d/Rmb3JcyvK4b5ex5KJBxEaHYwSbAboR6+ai/aPvoncC2sCGCyb+hgSEN57+s7xLM38bOuUpL+OG 39gbMmarhc7dv38RZkOxWmf+u8xDu0LzEv99laqLFtL9runillJqNg6+h075NXUz82EY/y9Pdjv86 ZkjEU6qrtOMdMDOPe3F5oKP2E2BJ0BPs9nN+D27LzzUxkvUZ3uZvhaoXv9SXkLGiB+SaWNfVbE4HL l5fl0PTA==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1uyW7S-00CH0p-59; Tue, 16 Sep 2025 15:53:18 +0200 From: Luis Henriques To: Miklos Szeredi Cc: Bernd Schubert , Laura Promberger , Dave Chinner , Matt Harvey , linux-fsdevel@vger.kernel.org, kernel-dev@igalia.com, linux-kernel@vger.kernel.org, Luis Henriques Subject: [RFC PATCH v6 1/4] dcache: export shrink_dentry_list() and add new helper d_dispose_if_unused() Date: Tue, 16 Sep 2025 14:53:07 +0100 Message-ID: <20250916135310.51177-2-luis@igalia.com> In-Reply-To: <20250916135310.51177-1-luis@igalia.com> References: <20250916135310.51177-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add and export a new helper d_dispose_if_unused() which is simply a wrapper around to_shrink_list(), to add an entry to a dispose list if it's not used anymore. Also export shrink_dentry_list() to kill all dentries in a dispose list. Suggested-by: Miklos Szeredi Signed-off-by: Luis Henriques --- fs/dcache.c | 18 ++++++++++++------ include/linux/dcache.h | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 60046ae23d51..3adefe05583c 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1086,6 +1086,15 @@ struct dentry *d_find_alias_rcu(struct inode *inode) return de; } =20 +void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose) +{ + spin_lock(&dentry->d_lock); + if (!dentry->d_lockref.count) + to_shrink_list(dentry, dispose); + spin_unlock(&dentry->d_lock); +} +EXPORT_SYMBOL(d_dispose_if_unused); + /* * Try to kill dentries associated with this inode. * WARNING: you must own a reference to inode. @@ -1096,12 +1105,8 @@ void d_prune_aliases(struct inode *inode) struct dentry *dentry; =20 spin_lock(&inode->i_lock); - hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { - spin_lock(&dentry->d_lock); - if (!dentry->d_lockref.count) - to_shrink_list(dentry, &dispose); - spin_unlock(&dentry->d_lock); - } + hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) + d_dispose_if_unused(dentry, &dispose); spin_unlock(&inode->i_lock); shrink_dentry_list(&dispose); } @@ -1141,6 +1146,7 @@ void shrink_dentry_list(struct list_head *list) shrink_kill(dentry); } } +EXPORT_SYMBOL(shrink_dentry_list); =20 static enum lru_status dentry_lru_isolate(struct list_head *item, struct list_lru_one *lru, void *arg) diff --git a/include/linux/dcache.h b/include/linux/dcache.h index cc3e1c1a3454..4ef41a5debdc 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -265,6 +265,8 @@ extern void d_tmpfile(struct file *, struct inode *); =20 extern struct dentry *d_find_alias(struct inode *); extern void d_prune_aliases(struct inode *); +extern void d_dispose_if_unused(struct dentry *, struct list_head *); +extern void shrink_dentry_list(struct list_head *); =20 extern struct dentry *d_find_alias_rcu(struct inode *); From nobody Thu Oct 2 13:03:49 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16F7D280A56; Tue, 16 Sep 2025 13:53:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030813; cv=none; b=Sn5y5j57zUgSG+BuEGJUi8Y93gogVB5vVvWA8twVJqC9gA1aJOCNGKSLqGQ1HXrqjBur3fiuj5COS4WBLczt+sIqteywjEgqzacZVO+alQxFLQm2kkzhu1aAteKJoRDKSE+1Y5uei8a+sjtHgXW5E7d7uBiksghtGcO/ZVCfcN4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030813; c=relaxed/simple; bh=ey3QI2O8xbexbjj2lzhcybZNAsBQVeMYDniWM4Ti+qA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DCvqcvvoYM1foCZIpObsYh3Tv5xR6w/ni09lMBgts8E7T2CZLGZXUTMJqhF0SMBT8I1OTHtEf4T1E0IEvFaRaNsPe6BSRvze1I4XnzC+15ZkHyagp+q/tkZwFnxHw+35XT753TosdTYrbeSYqVXcQqKB+mL9FIpw4y0NOyzYtTI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=EqkMsZyW; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="EqkMsZyW" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=tDBARSDEyWPHsPyE4kya3wzrOFMq8CWupsWD4ape51I=; b=EqkMsZyWe3X2hGupi5hYBo4hGV +ItO29sf/hNCucYANDJNCJhetlrNQIBRRWFEQhHbgq4bcYdnWe/U2ke0iQQ0DXTE8d5u1Z+gOU07n +T2TqEBNqzEchzfBMD9iFJlIBto1jdbVfWzwL4tmUcK27OuC3GIwEZDYdODtmKpVTtfOvbVUepWvX OJPaKfIydt8hY8FVdzfLbEm5HNzBNNKLq8k7L/YG5WewIxTST2jowo+P0a+tgWnheS54lEtT80z+Q mymaR+roIruYWMP9aL8NCwcN72F+Ab4iBHRV8rUaeywrfnQ13xo98IUartFSbQLZC+V7yTu3559Wl +OxZRYZw==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1uyW7S-00CH0r-Jy; Tue, 16 Sep 2025 15:53:18 +0200 From: Luis Henriques To: Miklos Szeredi Cc: Bernd Schubert , Laura Promberger , Dave Chinner , Matt Harvey , linux-fsdevel@vger.kernel.org, kernel-dev@igalia.com, linux-kernel@vger.kernel.org, Luis Henriques Subject: [RFC PATCH v6 2/4] fuse: new work queue to periodically invalidate expired dentries Date: Tue, 16 Sep 2025 14:53:08 +0100 Message-ID: <20250916135310.51177-3-luis@igalia.com> In-Reply-To: <20250916135310.51177-1-luis@igalia.com> References: <20250916135310.51177-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds the necessary infrastructure to keep track of all dentries created for FUSE file systems. A set of rbtrees, protected by hashed locks, will be used to keep all these dentries sorted by expiry time. A new module parameter 'inval_wq' is also added. When set, it will start a work queue which will periodically invalidate expired dentries. The value of this new parameter is the period, in seconds, for this work queue. Once this parameter is set, every new dentry will be added to one of the rbtrees. When the work queue is executed, it will check all the rbtrees and will invalidate those dentries that have timed-out. The work queue period can not be smaller than 5 seconds, but can be disabled by setting 'inval_wq' to zero (which is the default). Signed-off-by: Luis Henriques --- fs/fuse/dir.c | 216 ++++++++++++++++++++++++++++++++++++++++++----- fs/fuse/fuse_i.h | 10 +++ fs/fuse/inode.c | 3 + 3 files changed, 208 insertions(+), 21 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5c569c3cb53f..3e88da803ba6 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -27,6 +27,67 @@ module_param(allow_sys_admin_access, bool, 0644); MODULE_PARM_DESC(allow_sys_admin_access, "Allow users with CAP_SYS_ADMIN in initial userns to bypass allow_other= access check"); =20 +struct dentry_bucket { + struct rb_root tree; + spinlock_t lock; +}; + +#define HASH_BITS 5 +#define HASH_SIZE (1 << HASH_BITS) +static struct dentry_bucket dentry_hash[HASH_SIZE]; +struct delayed_work dentry_tree_work; + +/* Minimum invalidation work queue frequency */ +#define FUSE_DENTRY_INVAL_FREQ_MIN 5 + +unsigned __read_mostly inval_wq; +static int inval_wq_set(const char *val, const struct kernel_param *kp) +{ + unsigned int num; + unsigned int old =3D inval_wq; + int ret; + + if (!val) + return -EINVAL; + + ret =3D kstrtouint(val, 0, &num); + if (ret) + return ret; + + if ((num < FUSE_DENTRY_INVAL_FREQ_MIN) && (num !=3D 0)) + return -EINVAL; + + /* This should prevent overflow in secs_to_jiffies() */ + if (num > USHRT_MAX) + return -EINVAL; + + *((unsigned int *)kp->arg) =3D num; + + if (num && !old) + schedule_delayed_work(&dentry_tree_work, + secs_to_jiffies(num)); + else if (!num && old) + cancel_delayed_work_sync(&dentry_tree_work); + + return 0; +} +static const struct kernel_param_ops inval_wq_ops =3D { + .set =3D inval_wq_set, + .get =3D param_get_uint, +}; +module_param_cb(inval_wq, &inval_wq_ops, &inval_wq, 0644); +__MODULE_PARM_TYPE(inval_wq, "uint"); +MODULE_PARM_DESC(inval_wq, + "Dentries invalidation work queue period in secs (>=3D " + __stringify(FUSE_DENTRY_INVAL_FREQ_MIN) ")."); + +static inline struct dentry_bucket *get_dentry_bucket(struct dentry *dentr= y) +{ + int i =3D hash_ptr(dentry, HASH_BITS); + + return &dentry_hash[i]; +} + static void fuse_advise_use_readdirplus(struct inode *dir) { struct fuse_inode *fi =3D get_fuse_inode(dir); @@ -34,33 +95,131 @@ static void fuse_advise_use_readdirplus(struct inode *= dir) set_bit(FUSE_I_ADVISE_RDPLUS, &fi->state); } =20 -#if BITS_PER_LONG >=3D 64 -static inline void __fuse_dentry_settime(struct dentry *entry, u64 time) +struct fuse_dentry { + u64 time; + union { + struct rcu_head rcu; + struct rb_node node; + }; + struct dentry *dentry; +}; + +static void __fuse_dentry_tree_del_node(struct fuse_dentry *fd, + struct dentry_bucket *bucket) { - entry->d_fsdata =3D (void *) time; + if (!RB_EMPTY_NODE(&fd->node)) { + rb_erase(&fd->node, &bucket->tree); + RB_CLEAR_NODE(&fd->node); + } } =20 -static inline u64 fuse_dentry_time(const struct dentry *entry) +static void fuse_dentry_tree_del_node(struct dentry *dentry) { - return (u64)entry->d_fsdata; + struct fuse_dentry *fd =3D dentry->d_fsdata; + struct dentry_bucket *bucket =3D get_dentry_bucket(dentry); + + spin_lock(&bucket->lock); + __fuse_dentry_tree_del_node(fd, bucket); + spin_unlock(&bucket->lock); } =20 -#else -union fuse_dentry { - u64 time; - struct rcu_head rcu; -}; +static void fuse_dentry_tree_add_node(struct dentry *dentry) +{ + struct fuse_dentry *fd =3D dentry->d_fsdata; + struct dentry_bucket *bucket; + struct fuse_dentry *cur; + struct rb_node **p, *parent =3D NULL; + + if (!inval_wq) + return; + + bucket =3D get_dentry_bucket(dentry); + + spin_lock(&bucket->lock); + + __fuse_dentry_tree_del_node(fd, bucket); + + p =3D &bucket->tree.rb_node; + while (*p) { + parent =3D *p; + cur =3D rb_entry(*p, struct fuse_dentry, node); + if (fd->time < cur->time) + p =3D &(*p)->rb_left; + else + p =3D &(*p)->rb_right; + } + rb_link_node(&fd->node, parent, p); + rb_insert_color(&fd->node, &bucket->tree); + spin_unlock(&bucket->lock); +} + +/* + * work queue which, when enabled, will periodically check for expired den= tries + * in the dentries tree. + */ +static void fuse_dentry_tree_work(struct work_struct *work) +{ + LIST_HEAD(dispose); + struct fuse_dentry *fd; + struct rb_node *node; + int i; + + for (i =3D 0; i < HASH_SIZE; i++) { + spin_lock(&dentry_hash[i].lock); + node =3D rb_first(&dentry_hash[i].tree); + while (node) { + fd =3D rb_entry(node, struct fuse_dentry, node); + if (time_after64(get_jiffies_64(), fd->time)) { + rb_erase(&fd->node, &dentry_hash[i].tree); + RB_CLEAR_NODE(&fd->node); + spin_unlock(&dentry_hash[i].lock); + d_dispose_if_unused(fd->dentry, &dispose); + cond_resched(); + spin_lock(&dentry_hash[i].lock); + } else + break; + node =3D rb_first(&dentry_hash[i].tree); + } + spin_unlock(&dentry_hash[i].lock); + shrink_dentry_list(&dispose); + } + + if (inval_wq) + schedule_delayed_work(&dentry_tree_work, + secs_to_jiffies(inval_wq)); +} + +void fuse_dentry_tree_init(void) +{ + int i; + + for (i =3D 0; i < HASH_SIZE; i++) { + spin_lock_init(&dentry_hash[i].lock); + dentry_hash[i].tree =3D RB_ROOT; + } + INIT_DELAYED_WORK(&dentry_tree_work, fuse_dentry_tree_work); +} + +void fuse_dentry_tree_cleanup(void) +{ + int i; + + inval_wq =3D 0; + cancel_delayed_work_sync(&dentry_tree_work); + + for (i =3D 0; i < HASH_SIZE; i++) + WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree)); +} =20 static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time) { - ((union fuse_dentry *) dentry->d_fsdata)->time =3D time; + ((struct fuse_dentry *) dentry->d_fsdata)->time =3D time; } =20 static inline u64 fuse_dentry_time(const struct dentry *entry) { - return ((union fuse_dentry *) entry->d_fsdata)->time; + return ((struct fuse_dentry *) entry->d_fsdata)->time; } -#endif =20 static void fuse_dentry_settime(struct dentry *dentry, u64 time) { @@ -81,6 +240,7 @@ static void fuse_dentry_settime(struct dentry *dentry, u= 64 time) } =20 __fuse_dentry_settime(dentry, time); + fuse_dentry_tree_add_node(dentry); } =20 /* @@ -283,21 +443,36 @@ static int fuse_dentry_revalidate(struct inode *dir, = const struct qstr *name, goto out; } =20 -#if BITS_PER_LONG < 64 static int fuse_dentry_init(struct dentry *dentry) { - dentry->d_fsdata =3D kzalloc(sizeof(union fuse_dentry), - GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE); + struct fuse_dentry *fd; =20 - return dentry->d_fsdata ? 0 : -ENOMEM; + fd =3D kzalloc(sizeof(struct fuse_dentry), + GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE); + if (!fd) + return -ENOMEM; + + fd->dentry =3D dentry; + RB_CLEAR_NODE(&fd->node); + dentry->d_fsdata =3D fd; + + return 0; +} + +static void fuse_dentry_prune(struct dentry *dentry) +{ + struct fuse_dentry *fd =3D dentry->d_fsdata; + + if (!RB_EMPTY_NODE(&fd->node)) + fuse_dentry_tree_del_node(dentry); } + static void fuse_dentry_release(struct dentry *dentry) { - union fuse_dentry *fd =3D dentry->d_fsdata; + struct fuse_dentry *fd =3D dentry->d_fsdata; =20 kfree_rcu(fd, rcu); } -#endif =20 static int fuse_dentry_delete(const struct dentry *dentry) { @@ -331,10 +506,9 @@ static struct vfsmount *fuse_dentry_automount(struct p= ath *path) const struct dentry_operations fuse_dentry_operations =3D { .d_revalidate =3D fuse_dentry_revalidate, .d_delete =3D fuse_dentry_delete, -#if BITS_PER_LONG < 64 .d_init =3D fuse_dentry_init, + .d_prune =3D fuse_dentry_prune, .d_release =3D fuse_dentry_release, -#endif .d_automount =3D fuse_dentry_automount, }; =20 diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index cc428d04be3e..b34be6f95bbe 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -54,6 +54,13 @@ /** Frequency (in jiffies) of request timeout checks, if opted into */ extern const unsigned long fuse_timeout_timer_freq; =20 +/* + * Dentries invalidation workqueue period, in seconds. The value of this + * parameter shall be >=3D FUSE_DENTRY_INVAL_FREQ_MIN seconds, or 0 (zero)= , in + * which case no workqueue will be created. + */ +extern unsigned inval_wq __read_mostly; + /** Maximum of max_pages received in init_out */ extern unsigned int fuse_max_pages_limit; /* @@ -1266,6 +1273,9 @@ void fuse_wait_aborted(struct fuse_conn *fc); /* Check if any requests timed out */ void fuse_check_timeout(struct work_struct *work); =20 +void fuse_dentry_tree_init(void); +void fuse_dentry_tree_cleanup(void); + /** * Invalidate inode attributes */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7ddfd2b3cc9c..db275a04021d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -2256,6 +2256,8 @@ static int __init fuse_init(void) if (res) goto err_sysfs_cleanup; =20 + fuse_dentry_tree_init(); + sanitize_global_limit(&max_user_bgreq); sanitize_global_limit(&max_user_congthresh); =20 @@ -2275,6 +2277,7 @@ static void __exit fuse_exit(void) { pr_debug("exit\n"); =20 + fuse_dentry_tree_cleanup(); fuse_ctl_cleanup(); fuse_sysfs_cleanup(); fuse_fs_cleanup(); From nobody Thu Oct 2 13:03:49 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1700031D731; Tue, 16 Sep 2025 13:53:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030815; cv=none; b=KJxcyLbcbt567P5JBr5JsZLkq4lk1kMoyg5/u2goWq2TkX5N2p34x7FW2L1QzUJWTSpir6NN9C0GGwbDbtrCxH2GCSXKHUYTprcDmLMaR2+yI6p4XSsDLrBp5pusp8XbU1SiGdRMXxz6fQS6dmQJOCxc+Cw0YLKLTMLjRMXGQH4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030815; c=relaxed/simple; bh=cC4ESHmRoPHFx9+ziH5e9pMEerTGhGTntMX0FCi5StQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=E+w34OuDzIRvDa37s6jb64s7x4TNLpI8SK8Xva2ZUC3mFHcErhU6g1Hao50PfJBxNUCvpQO/bkj+0thJghURWoMFdddbZGvze/RtxfHjYjNjOdIBEerPweIquZolcG8LI4D10c93Hhn6vR9o4T/QIOnyI3jwVfTuMX60vTvmsOc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=Nqo78lpt; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="Nqo78lpt" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=Md8EXoCxaeKBb52Cypj7dZ5knzMrAkQ9Bak1BFT2NZg=; b=Nqo78lptMaUCsmy2zv8wIyMusw xLiTGvEazTZdr0atiQ5uxYY/mhe8iBr0nCtgPvyk3puZQjbKV/eq3pVtqgrD8PvZq7h5Y3JcnSHHY rwn3eWhF87Wn5Q+gZvQ2hYvf1a3hBYHU1r1rbiTVR/iOWuUxg/C/1ndnRPxO+8uvgTq2Hig+ujp+L EjuL95hqmZAIvHEQ3w8i22EcHpf8IVNWzAEhFbR0BHqrhnWxINkuTvP5xeUIDFDrr1VPqO5prVH/F PQ03vYWt36jOTRK1Tv9MC+c2cYz+GF6+OLcn9vpPd0ac63JXigZ4RObExlxD/QKy4Zr5RSmjHmp8O FnRim75A==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1uyW7T-00CH0u-2T; Tue, 16 Sep 2025 15:53:19 +0200 From: Luis Henriques To: Miklos Szeredi Cc: Bernd Schubert , Laura Promberger , Dave Chinner , Matt Harvey , linux-fsdevel@vger.kernel.org, kernel-dev@igalia.com, linux-kernel@vger.kernel.org, Luis Henriques Subject: [RFC PATCH v6 3/4] fuse: new work queue to invalidate dentries from old epochs Date: Tue, 16 Sep 2025 14:53:09 +0100 Message-ID: <20250916135310.51177-4-luis@igalia.com> In-Reply-To: <20250916135310.51177-1-luis@igalia.com> References: <20250916135310.51177-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- fs/fuse/dev.c | 7 ++++--- fs/fuse/dir.c | 21 +++++++++++++++++++++ fs/fuse/fuse_i.h | 4 ++++ fs/fuse/inode.c | 2 ++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 5150aa25e64b..c24e78f86b50 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2033,13 +2033,14 @@ static int fuse_notify_resend(struct fuse_conn *fc) =20 /* * 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); =20 return 0; } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 3e88da803ba6..67e3340a443c 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)); } =20 +void fuse_epoch_work(struct work_struct *work) +{ + struct fuse_conn *fc =3D container_of(work, struct fuse_conn, + epoch_work); + struct fuse_mount *fm; + struct inode *inode; + + down_read(&fc->killsb); + + inode =3D 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 b34be6f95bbe..53ca87814ef3 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; =20 + struct work_struct epoch_work; + struct rcu_head rcu; =20 /** The user id for this mount */ @@ -1276,6 +1278,8 @@ void fuse_check_timeout(struct work_struct *work); void fuse_dentry_tree_init(void); void fuse_dentry_tree_cleanup(void); =20 +void fuse_epoch_work(struct work_struct *work); + /** * Invalidate inode attributes */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index db275a04021d..c054f02e661d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -967,6 +967,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_m= ount *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); @@ -1019,6 +1020,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); From nobody Thu Oct 2 13:03:49 2025 Received: from fanzine2.igalia.com (fanzine2.igalia.com [213.97.179.56]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 971D931E10D; Tue, 16 Sep 2025 13:53:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=213.97.179.56 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030816; cv=none; b=Bo01P3vpKtVPdfYbA6Nbgne6iDiP2NsfzwQmohBajlvqVBi/Yi3We/XyGGK6M9xCgtrhgPC7niGFzPzJBheRN8EPWFrNlHzwflgF2mgMaIDUroBKwPYYw0hdJCbW3kS1gm/rEYA7Ng55gq/9qRcN5YzsGBrxODU0ntCPe2WzApY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758030816; c=relaxed/simple; bh=ObnaGSSBvgRTHsZ5n03hXV6xIAMyav8R1ErbqsEiEhY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kUk5xyf0SRkvLSjDBNyjjlW54WAwzePQ964320pdHY1ehf8chZSSzSYAFdImsUyFmGH6XbXQsWjH70fYlJGApJj3JWa4HRsQZ15O7ih2QHVUHWcoUBDR+l8D9UFSsPFie0jf8WBL2s/hw62SU03/YxmQbMRlaiPbTulKZ4Twh7w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com; spf=pass smtp.mailfrom=igalia.com; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b=WbSYwM5K; arc=none smtp.client-ip=213.97.179.56 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=igalia.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=igalia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=igalia.com header.i=@igalia.com header.b="WbSYwM5K" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=igalia.com; s=20170329; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=tmF4OccLdgsVZtebYdUSAH/Uj8VZI2v9HZazcjtFpKE=; b=WbSYwM5K/xXnlv6OFRqIKbvhTE Q7GNx330eOSHOZYHyRpwGioaWEfeaOJvkTTkIy3gfbt4aK9oI2DTwNGCkk3sFvjfc4Njt447sXY00 gt/JISVukiRQyueQmJN0195DChxDn69Q2tx91EZOarn4qDJKJy+6oTBZ9KCsJIl7zM1rD1YLwAr/J nTNZkeKbBfwXyilPt4ryKIkRg0fXKEF9HbiFHaZNUrVzvczU6mK7dRZdqjGAZH6JrPYMytQSj/Ny4 6+36AcSI9k2byN6M595xNzEf3bY/Ni7dFUiznFX2S3KYjAu8qFHqdihvgC8lzbFUz+VQeWOKysKOw 4iVBYcow==; Received: from bl17-145-117.dsl.telepac.pt ([188.82.145.117] helo=localhost) by fanzine2.igalia.com with utf8esmtpsa (Cipher TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim) id 1uyW7T-00CH0w-H5; Tue, 16 Sep 2025 15:53:19 +0200 From: Luis Henriques To: Miklos Szeredi Cc: Bernd Schubert , Laura Promberger , Dave Chinner , Matt Harvey , linux-fsdevel@vger.kernel.org, kernel-dev@igalia.com, linux-kernel@vger.kernel.org, Luis Henriques Subject: [RFC PATCH v6 4/4] fuse: refactor fuse_conn_put() to remove negative logic. Date: Tue, 16 Sep 2025 14:53:10 +0100 Message-ID: <20250916135310.51177-5-luis@igalia.com> In-Reply-To: <20250916135310.51177-1-luis@igalia.com> References: <20250916135310.51177-1-luis@igalia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" There is no functional change with this patch. It simply refactors function fuse_conn_put() to not use negative logic, which makes it more easier to read. Signed-off-by: Luis Henriques --- fs/fuse/inode.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c054f02e661d..80cce3bb6b00 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1012,27 +1012,28 @@ static void delayed_release(struct rcu_head *p) =20 void fuse_conn_put(struct fuse_conn *fc) { - if (refcount_dec_and_test(&fc->count)) { - struct fuse_iqueue *fiq =3D &fc->iq; - struct fuse_sync_bucket *bucket; - - if (IS_ENABLED(CONFIG_FUSE_DAX)) - 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); - bucket =3D rcu_dereference_protected(fc->curr_bucket, 1); - if (bucket) { - WARN_ON(atomic_read(&bucket->count) !=3D 1); - kfree(bucket); - } - if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) - fuse_backing_files_free(fc); - call_rcu(&fc->rcu, delayed_release); + struct fuse_iqueue *fiq =3D &fc->iq; + struct fuse_sync_bucket *bucket; + + if (!refcount_dec_and_test(&fc->count)) + return; + + if (IS_ENABLED(CONFIG_FUSE_DAX)) + 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); + bucket =3D rcu_dereference_protected(fc->curr_bucket, 1); + if (bucket) { + WARN_ON(atomic_read(&bucket->count) !=3D 1); + kfree(bucket); } + if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) + fuse_backing_files_free(fc); + call_rcu(&fc->rcu, delayed_release); } EXPORT_SYMBOL_GPL(fuse_conn_put);