From nobody Sat Feb 7 08:43:21 2026 Received: from e2i340.smtp2go.com (e2i340.smtp2go.com [103.2.141.84]) (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 2FF583D2FF2 for ; Wed, 21 Jan 2026 20:13:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.2.141.84 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769026442; cv=none; b=fOcXdzjT6Nh9THyNx7PzD2f4fin79ptmUYof9vIIkUu7e1jAgL+0CN1p79l+jbkW578HlNvmGLBf9JmkubEeOemaG4GqCFosMBFWOvDwlm+lrb6nYsCFQBbLirdWmMA41gfF5aeJ5maLFjlTh/O3cl2WvW2GmfFc5G7LUbQTShY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769026442; c=relaxed/simple; bh=HFFmc2bQ44+vXSnb4hCLIXFD3EAFhi9L+6xYOwTyEqE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=f1fB4+7RH9rtqiG2OaiwSvLCeNmNmmxeVP9pjj8wsx1q7xNV+urOPaIBLzPSt0qJfAJv8dnnfdda3BAuiXrdvXDyAS7sc8h+Fi55RpX1WwO9CrZK3LKGIl03CPpXkXyzEsgEAnWKPN4EVEGNyWAacH5kXmAMS0T/Utcu/v5MaA8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=triplefau.lt; spf=pass smtp.mailfrom=em510616.triplefau.lt; dkim=fail (0-bit key) header.d=smtpservice.net header.i=@smtpservice.net header.b=jw17vZrQ reason="unknown key version"; dkim=pass (2048-bit key) header.d=triplefau.lt header.i=@triplefau.lt header.b=R0zm2T+5; arc=none smtp.client-ip=103.2.141.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=triplefau.lt Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=em510616.triplefau.lt Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="unknown key version" (0-bit key) header.d=smtpservice.net header.i=@smtpservice.net header.b="jw17vZrQ"; dkim=pass (2048-bit key) header.d=triplefau.lt header.i=@triplefau.lt header.b="R0zm2T+5" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=smtpservice.net; s=maxzs0.a1-4.dyn; x=1769027340; h=Feedback-ID: X-Smtpcorp-Track:Message-ID:Date:Subject:To:From:Reply-To:Sender: List-Unsubscribe:List-Unsubscribe-Post; bh=SmcpPM/RN2TjSvgJHnbIuizsKdkZm6oip0hpmozDlak=; b=jw17vZrQA+U7W8hueJCX9TIkqz XmfLElz9qY/w/ULYv9SqCwuAgmNFsQdoIqo7ah3hrBZuFELWgB9w/U+mHrXBZbkkLsIKvjRoEwLU4 RK/cDrGZ+JVTCwosjfjOA2YtHQnauHR4vVsqlLTMdm6m5IptxJ97I5D4KdBUcjC97eXjdEtz74LN5 zHmgkee8jCpIZfLThxwUink/X73+tEmL541jcf+/FS0xciXlpyWtc7bm8Wq09+Y5G2TwxmyRc567v 7KU0d0fNUng8YCuYbbjGHub+hEfvgs25QaXLebfpZrOCTuobsvABJYCKeEFGvmMdgoc1L0Lzi/m4m nwXQ0TiQ==; DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=triplefau.lt; i=@triplefau.lt; q=dns/txt; s=s510616; t=1769026440; h=from : subject : to : message-id : date; bh=SmcpPM/RN2TjSvgJHnbIuizsKdkZm6oip0hpmozDlak=; b=R0zm2T+5MyIr9H0SIGZqUKnjFompBf36sG6/uG63PWdi232vQPkbJ85iVR/Ei4FckiWLa BQXYF2WDspP45CpDx07doYYAyTefEpqteAW70bPEWaA2vu9RBPpeSh/4Ez1OuXPx9ita4+p 1/yTXe1GJZWCgX70OGamd0XW5UZir3/QQxG9gEsBkG0grujXWCFq1UM0t9kZyarb9T/RJi9 EkbGqyhIiCellVKRGqkr4IsHVd9w1jHj9qb5SxBcrpbE0ISneNtwYdgEbXPOqDXOKhd6wwT mRqosrsKGaqCFMxeEbJNdRi7MTHHP0hufkqaz4BV8dvu3SAgVc1PD9ehPjzA== Received: from [10.176.58.103] (helo=SmtpCorp) by smtpcorp.com with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.94.2-S2G) (envelope-from ) id 1vieaK-TRk26B-7S; Wed, 21 Jan 2026 20:13:48 +0000 Received: from [10.12.239.196] (helo=localhost) by smtpcorp.com with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.98.1-S2G) (envelope-from ) id 1vieaJ-4o5NDgruk6B-sIYn; Wed, 21 Jan 2026 20:13:47 +0000 From: Remi Pommarel To: v9fs@lists.linux.dev Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Van Hensbergen , Latchesar Ionkov , Dominique Martinet , Christian Schoenebeck , Remi Pommarel Subject: [PATCH v2 1/3] 9p: Cache negative dentries for lookup performance Date: Wed, 21 Jan 2026 20:56:08 +0100 Message-ID: <51afd44abb72d251e2022fbb4d53dd05a03aeed0.1769013622.git.repk@triplefau.lt> X-Mailer: git-send-email 2.50.1 In-Reply-To: References: 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 X-Smtpcorp-Track: NxctVlrPqeBb.jNXSNKRzE93i.FeZo7g1FPLK Feedback-ID: 510616m:510616apGKSTK:510616snr-4OLm47 X-Report-Abuse: Please forward a copy of this message, including all headers, to Content-Type: text/plain; charset="utf-8" Not caching negative dentries can result in poor performance for workloads that repeatedly look up non-existent paths. Each such lookup triggers a full 9P transaction with the server, adding unnecessary overhead. A typical example is source compilation, where multiple cc1 processes are spawned and repeatedly search for the same missing header files over and over again. This change enables caching of negative dentries, so that lookups for known non-existent paths do not require a full 9P transaction. The cached negative dentries are retained for a configurable duration (expressed in milliseconds), as specified by the ndentry_timeout field in struct v9fs_session_info. If set to -1, negative dentries are cached indefinitely. This optimization reduces lookup overhead and improves performance for workloads involving frequent access to non-existent paths. Signed-off-by: Remi Pommarel Tested-by: Christian Schoenebeck --- fs/9p/fid.c | 11 +++-- fs/9p/v9fs.c | 1 + fs/9p/v9fs.h | 2 + fs/9p/v9fs_vfs.h | 15 ++++++ fs/9p/vfs_dentry.c | 105 ++++++++++++++++++++++++++++++++++------ fs/9p/vfs_inode.c | 7 +-- fs/9p/vfs_super.c | 1 + include/net/9p/client.h | 2 + 8 files changed, 122 insertions(+), 22 deletions(-) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index f84412290a30..76242d450aa7 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -20,7 +20,9 @@ =20 static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid) { - hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata); + struct v9fs_dentry *v9fs_dentry =3D to_v9fs_dentry(dentry); + + hlist_add_head(&fid->dlist, &v9fs_dentry->head); } =20 =20 @@ -112,6 +114,7 @@ void v9fs_open_fid_add(struct inode *inode, struct p9_f= id **pfid) =20 static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int= any) { + struct v9fs_dentry *v9fs_dentry =3D to_v9fs_dentry(dentry); struct p9_fid *fid, *ret; =20 p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p) uid %d any %d\n", @@ -119,11 +122,9 @@ static struct p9_fid *v9fs_fid_find(struct dentry *den= try, kuid_t uid, int any) any); ret =3D NULL; /* we'll recheck under lock if there's anything to look in */ - if (dentry->d_fsdata) { - struct hlist_head *h =3D (struct hlist_head *)&dentry->d_fsdata; - + if (!hlist_empty(&v9fs_dentry->head)) { spin_lock(&dentry->d_lock); - hlist_for_each_entry(fid, h, dlist) { + hlist_for_each_entry(fid, &v9fs_dentry->head, dlist) { if (any || uid_eq(fid->uid, uid)) { ret =3D fid; p9_fid_get(ret); diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 057487efaaeb..1da7ab186478 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -422,6 +422,7 @@ static void v9fs_apply_options(struct v9fs_session_info= *v9ses, v9ses->cache =3D ctx->session_opts.cache; v9ses->uid =3D ctx->session_opts.uid; v9ses->session_lock_timeout =3D ctx->session_opts.session_lock_timeout; + v9ses->ndentry_timeout =3D ctx->session_opts.ndentry_timeout; } =20 /** diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 6a12445d3858..99d1a0ff3368 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -91,6 +91,7 @@ enum p9_cache_bits { * @debug: debug level * @afid: authentication handle * @cache: cache mode of type &p9_cache_bits + * @ndentry_timeout: Negative dentry lookup cache retention time in ms * @cachetag: the tag of the cache associated with this session * @fscache: session cookie associated with FS-Cache * @uname: string user name to mount hierarchy as @@ -116,6 +117,7 @@ struct v9fs_session_info { unsigned short debug; unsigned int afid; unsigned int cache; + unsigned int ndentry_timeout; #ifdef CONFIG_9P_FSCACHE char *cachetag; struct fscache_volume *fscache; diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index d3aefbec4de6..7e6e8881081c 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -28,6 +28,19 @@ /* flags for v9fs_stat2inode() & v9fs_stat2inode_dotl() */ #define V9FS_STAT2INODE_KEEP_ISIZE 1 =20 +/** + * struct v9fs_dentry - v9fs specific dentry data + * @head: List of fid associated with this dentry + * @expire_time: Lookup cache expiration time for negative dentries + * @rcu: used by kfree_rcu to schedule clean up job + */ +struct v9fs_dentry { + struct hlist_head head; + u64 expire_time; + struct rcu_head rcu; +}; +#define to_v9fs_dentry(d) ((struct v9fs_dentry *)((d)->d_fsdata)) + extern struct file_system_type v9fs_fs_type; extern const struct address_space_operations v9fs_addr_operations; extern const struct file_operations v9fs_file_operations; @@ -35,6 +48,8 @@ extern const struct file_operations v9fs_file_operations_= dotl; extern const struct file_operations v9fs_dir_operations; extern const struct file_operations v9fs_dir_operations_dotl; extern const struct dentry_operations v9fs_dentry_operations; +extern void v9fs_dentry_refresh(struct dentry *dentry); +extern void v9fs_dentry_fid_remove(struct dentry *dentry); extern const struct dentry_operations v9fs_cached_dentry_operations; extern struct kmem_cache *v9fs_inode_cache; =20 diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index c5bf74d547e8..90291cf0a34b 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -23,6 +23,46 @@ #include "v9fs_vfs.h" #include "fid.h" =20 +/** + * v9fs_dentry_is_expired - Check if dentry lookup has expired + * + * This should be called to know if a negative dentry should be removed fr= om + * cache. + * + * @dentry: dentry in question + * + */ +static bool v9fs_dentry_is_expired(struct dentry const *dentry) +{ + struct v9fs_session_info *v9ses =3D v9fs_dentry2v9ses(dentry); + struct v9fs_dentry *v9fs_dentry =3D to_v9fs_dentry(dentry); + + if (v9ses->ndentry_timeout =3D=3D -1) + return false; + + return time_before_eq64(v9fs_dentry->expire_time, get_jiffies_64()); +} + +/** + * v9fs_dentry_refresh - Refresh dentry lookup cache timeout + * + * This should be called when a look up yields a negative entry. + * + * @dentry: dentry in question + * + */ +void v9fs_dentry_refresh(struct dentry *dentry) +{ + struct v9fs_session_info *v9ses =3D v9fs_dentry2v9ses(dentry); + struct v9fs_dentry *v9fs_dentry =3D to_v9fs_dentry(dentry); + + if (v9ses->ndentry_timeout =3D=3D -1) + return; + + v9fs_dentry->expire_time =3D get_jiffies_64() + + msecs_to_jiffies(v9ses->ndentry_timeout); +} + /** * v9fs_cached_dentry_delete - called when dentry refcount equals 0 * @dentry: dentry in question @@ -33,20 +73,15 @@ static int v9fs_cached_dentry_delete(const struct dentr= y *dentry) p9_debug(P9_DEBUG_VFS, " dentry: %pd (%p)\n", dentry, dentry); =20 - /* Don't cache negative dentries */ - if (d_really_is_negative(dentry)) - return 1; - return 0; -} + if (!d_really_is_negative(dentry)) + return 0; =20 -/** - * v9fs_dentry_release - called when dentry is going to be freed - * @dentry: dentry that is being release - * - */ + return v9fs_dentry_is_expired(dentry); +} =20 -static void v9fs_dentry_release(struct dentry *dentry) +static void __v9fs_dentry_fid_remove(struct dentry *dentry) { + struct v9fs_dentry *v9fs_dentry =3D to_v9fs_dentry(dentry); struct hlist_node *p, *n; struct hlist_head head; =20 @@ -54,13 +89,54 @@ static void v9fs_dentry_release(struct dentry *dentry) dentry, dentry); =20 spin_lock(&dentry->d_lock); - hlist_move_list((struct hlist_head *)&dentry->d_fsdata, &head); + hlist_move_list(&v9fs_dentry->head, &head); spin_unlock(&dentry->d_lock); =20 hlist_for_each_safe(p, n, &head) p9_fid_put(hlist_entry(p, struct p9_fid, dlist)); } =20 +/** + * v9fs_dentry_fid_remove - Release all dentry's fid + * @dentry: dentry in question + * + */ +void v9fs_dentry_fid_remove(struct dentry *dentry) +{ + __v9fs_dentry_fid_remove(dentry); +} + +/** + * v9fs_dentry_init - Initialize v9fs dentry data + * @dentry: dentry in question + * + */ +static int v9fs_dentry_init(struct dentry *dentry) +{ + struct v9fs_dentry *v9fs_dentry =3D kzalloc(sizeof(*v9fs_dentry), + GFP_KERNEL); + + if (!v9fs_dentry) + return -ENOMEM; + + INIT_HLIST_HEAD(&v9fs_dentry->head); + dentry->d_fsdata =3D (void *)v9fs_dentry; + return 0; +} + +/** + * v9fs_dentry_release - called when dentry is going to be freed + * @dentry: dentry that is being release + * + */ +static void v9fs_dentry_release(struct dentry *dentry) +{ + struct v9fs_dentry *v9fs_dentry =3D to_v9fs_dentry(dentry); + + __v9fs_dentry_fid_remove(dentry); + kfree_rcu(v9fs_dentry, rcu); +} + static int __v9fs_lookup_revalidate(struct dentry *dentry, unsigned int fl= ags) { struct p9_fid *fid; @@ -72,7 +148,7 @@ static int __v9fs_lookup_revalidate(struct dentry *dentr= y, unsigned int flags) =20 inode =3D d_inode(dentry); if (!inode) - goto out_valid; + return !v9fs_dentry_is_expired(dentry); =20 v9inode =3D V9FS_I(inode); if (v9inode->cache_validity & V9FS_INO_INVALID_ATTR) { @@ -112,7 +188,6 @@ static int __v9fs_lookup_revalidate(struct dentry *dent= ry, unsigned int flags) return retval; } } -out_valid: p9_debug(P9_DEBUG_VFS, "dentry: %pd (%p) is valid\n", dentry, dentry); return 1; } @@ -139,12 +214,14 @@ const struct dentry_operations v9fs_cached_dentry_ope= rations =3D { .d_revalidate =3D v9fs_lookup_revalidate, .d_weak_revalidate =3D __v9fs_lookup_revalidate, .d_delete =3D v9fs_cached_dentry_delete, + .d_init =3D v9fs_dentry_init, .d_release =3D v9fs_dentry_release, .d_unalias_trylock =3D v9fs_dentry_unalias_trylock, .d_unalias_unlock =3D v9fs_dentry_unalias_unlock, }; =20 const struct dentry_operations v9fs_dentry_operations =3D { + .d_init =3D v9fs_dentry_init, .d_release =3D v9fs_dentry_release, .d_unalias_trylock =3D v9fs_dentry_unalias_trylock, .d_unalias_unlock =3D v9fs_dentry_unalias_unlock, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 0f3189a0a516..a82a71be309b 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -549,7 +549,7 @@ static int v9fs_remove(struct inode *dir, struct dentry= *dentry, int flags) =20 /* invalidate all fids associated with dentry */ /* NOTE: This will not include open fids */ - dentry->d_op->d_release(dentry); + v9fs_dentry_fid_remove(dentry); } return retval; } @@ -732,9 +732,10 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, stru= ct dentry *dentry, name =3D dentry->d_name.name; fid =3D p9_client_walk(dfid, 1, &name, 1); p9_fid_put(dfid); - if (fid =3D=3D ERR_PTR(-ENOENT)) + if (fid =3D=3D ERR_PTR(-ENOENT)) { inode =3D NULL; - else if (IS_ERR(fid)) + v9fs_dentry_refresh(dentry); + } else if (IS_ERR(fid)) inode =3D ERR_CAST(fid); else if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) inode =3D v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb); diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 315336de6f02..9d9360f9e502 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -327,6 +327,7 @@ static int v9fs_init_fs_context(struct fs_context *fc) ctx->session_opts.uid =3D INVALID_UID; ctx->session_opts.dfltuid =3D V9FS_DEFUID; ctx->session_opts.dfltgid =3D V9FS_DEFGID; + ctx->session_opts.ndentry_timeout =3D 0; =20 /* initialize client options */ ctx->client_opts.proto_version =3D p9_proto_2000L; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 838a94218b59..3d2483db9259 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -192,6 +192,7 @@ struct p9_rdma_opts { * @dfltgid: default numeric groupid to mount hierarchy as * @uid: if %V9FS_ACCESS_SINGLE, the numeric uid which mounted the hierarc= hy * @session_lock_timeout: retry interval for blocking locks + * @ndentry_timeout: Negative dentry lookup cache retention time in ms * * This strucure holds options which are parsed and will be transferred * to the v9fs_session_info structure when mounted, and therefore largely @@ -212,6 +213,7 @@ struct p9_session_opts { kgid_t dfltgid; kuid_t uid; long session_lock_timeout; + unsigned int ndentry_timeout; }; =20 /* Used by mount API to store parsed mount options */ --=20 2.50.1 From nobody Sat Feb 7 08:43:21 2026 Received: from e2i340.smtp2go.com (e2i340.smtp2go.com [103.2.141.84]) (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 2FFE13D6694 for ; Wed, 21 Jan 2026 20:13:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.2.141.84 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769026441; cv=none; b=s/DyNByf9UK7dt+LcNDKIci3gbJTOwnw672isfCla9B1/PsOBc0onKrraJ2/Sw0aivwk6CNKq1FaecvwQnpy/iwrCdzI7UjRy94ThFc2hra2Zt22fqF2Wm4H0g+V0IlD9otBKNCSSoi5VyHxTyR6aAZEZCD6dtOb1AQwJauX5pY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769026441; c=relaxed/simple; bh=MI2BmTqtZrNSQrlrrLbu/L5u8kVcj0qImD2nnPGSwwo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HZ2e9d2hXhn+ORoAkEQZcT4PJhY3yWNLUQPped+iDcYuJAASwKEpe9wfILtWwPo6bVi7Y2hGp8LjnvTa36DnjCkka5PtIfRmywzcxKd4DjzzT2yS+Q3rIU/c1W5MoGUco006/5MVU3iiQWUvTh30RRHDfuygAyFb0SqfHSiNx/o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=triplefau.lt; spf=pass smtp.mailfrom=em510616.triplefau.lt; dkim=fail (0-bit key) header.d=smtpservice.net header.i=@smtpservice.net header.b=MfeRhre4 reason="unknown key version"; dkim=pass (2048-bit key) header.d=triplefau.lt header.i=@triplefau.lt header.b=evPn6BU4; arc=none smtp.client-ip=103.2.141.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=triplefau.lt Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=em510616.triplefau.lt Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="unknown key version" (0-bit key) header.d=smtpservice.net header.i=@smtpservice.net header.b="MfeRhre4"; dkim=pass (2048-bit key) header.d=triplefau.lt header.i=@triplefau.lt header.b="evPn6BU4" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=smtpservice.net; s=maxzs0.a1-4.dyn; x=1769027340; h=Feedback-ID: X-Smtpcorp-Track:Message-ID:Date:Subject:To:From:Reply-To:Sender: List-Unsubscribe:List-Unsubscribe-Post; bh=T3TZwnO8y1oxOvrH2YQS/n2Bl9vhGxNTqyNbInimA3Q=; b=MfeRhre4nF/w+u5fEuSxaaNixL fyadY9bI4XhJpDmvBCa6MUeXSsD7pnqYa2IPXdzL7vKDauKZdC1wtRTLGmt3EJyXObzo/ONee87SV ezKyiskkt6UrZSVGeC0dX6m3UmTDfX8Wi/t8ovwKSMIc2rE/hZ02PLMlSNktvBhzyxht2TIAAT/xp yKQ2rHOmzMQzsy2z/RJ7Jebud6nq7E+TGh5+spZm2CtzipEXmJOJucXlcPin5Q2eFoT1gz5S5wHtI YEKVuiOQzox+iIiRZGQrKRCzAiT+4ANs+fawL6RIrHekA3rnPGw5zNj0wV8J9G8BtuaDs/Y5JVCXg l1OKViXw==; DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=triplefau.lt; i=@triplefau.lt; q=dns/txt; s=s510616; t=1769026440; h=from : subject : to : message-id : date; bh=T3TZwnO8y1oxOvrH2YQS/n2Bl9vhGxNTqyNbInimA3Q=; b=evPn6BU4N6MkN7lIh/N37fs4dcS6GOrLoBkVyz1TIOuvx3OqO4+uP//kv8eDkC1hxqIrB WsFZSjliK+EhRCZzPN6nR4FZP6MMjPjHOfrG0dt+zscTO+aWU637JnFkE2cNX3ZsUrnc3mD IHy3gZX9hCmJebfGWm8Y+wJZV+gOy0vflzKzlRplkVsgz9If4q7KjuCzG7vOqBCOuq4k1uN fJy+MIT6KqHy81MWrTs2TOEyeCual60v7UpltR9IPvkLVi1xOKN2vsKQC7oNAbECyRPtQ1U ODcxn2gzaNPyfwsMC+SVQXcAyr7NDttzIFhmxym0em/sktUjkOw+he+JDCxg== Received: from [10.139.162.187] (helo=SmtpCorp) by smtpcorp.com with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.94.2-S2G) (envelope-from ) id 1vieaL-TRk28y-OL; Wed, 21 Jan 2026 20:13:49 +0000 Received: from [10.12.239.196] (helo=localhost) by smtpcorp.com with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.98.1-S2G) (envelope-from ) id 1vieaL-4o5NDgrlA5K-l5yO; Wed, 21 Jan 2026 20:13:49 +0000 From: Remi Pommarel To: v9fs@lists.linux.dev Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Van Hensbergen , Latchesar Ionkov , Dominique Martinet , Christian Schoenebeck , Remi Pommarel Subject: [PATCH v2 2/3] 9p: Introduce option for negative dentry cache retention time Date: Wed, 21 Jan 2026 20:56:09 +0100 Message-ID: <7e38e7bd31674208ab2b0de909c0744feda2c03f.1769013622.git.repk@triplefau.lt> X-Mailer: git-send-email 2.50.1 In-Reply-To: References: 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 X-Smtpcorp-Track: dESi4y2bpk5E.Vr1dGdwNkxoC.pRDBTAd6cPk Feedback-ID: 510616m:510616apGKSTK:510616szNUyQSrVu X-Report-Abuse: Please forward a copy of this message, including all headers, to Content-Type: text/plain; charset="utf-8" Add support for a new mount option in v9fs that allows users to specify the duration for which negative dentries are retained in the cache. The retention time can be set in milliseconds using the ndentrytmo option. For the same consistency reasons, this option should only be used in exclusive or read-only mount scenarios, aligning with the cache=3Dloose usage. Signed-off-by: Remi Pommarel Tested-by: Christian Schoenebeck --- fs/9p/v9fs.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 1da7ab186478..f58a2718e412 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -39,7 +39,7 @@ enum { * source if we rejected it as EINVAL */ Opt_source, /* Options that take integer arguments */ - Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, + Opt_debug, Opt_dfltuid, Opt_dfltgid, Opt_afid, Opt_ndentrytmo, /* String options */ Opt_uname, Opt_remotename, Opt_cache, Opt_cachetag, /* Options that take no arguments */ @@ -93,6 +93,7 @@ const struct fs_parameter_spec v9fs_param_spec[] =3D { fsparam_string ("access", Opt_access), fsparam_flag ("posixacl", Opt_posixacl), fsparam_u32 ("locktimeout", Opt_locktimeout), + fsparam_s32 ("ndentrytmo", Opt_ndentrytmo), =20 /* client options */ fsparam_u32 ("msize", Opt_msize), @@ -159,6 +160,8 @@ int v9fs_show_options(struct seq_file *m, struct dentry= *root) from_kgid_munged(&init_user_ns, v9ses->dfltgid)); if (v9ses->afid !=3D ~0) seq_printf(m, ",afid=3D%u", v9ses->afid); + if (v9ses->ndentry_timeout !=3D 0) + seq_printf(m, ",ndentrytmo=3D%d", v9ses->ndentry_timeout); if (strcmp(v9ses->uname, V9FS_DEFUSER) !=3D 0) seq_printf(m, ",uname=3D%s", v9ses->uname); if (strcmp(v9ses->aname, V9FS_DEFANAME) !=3D 0) @@ -337,6 +340,10 @@ int v9fs_parse_param(struct fs_context *fc, struct fs_= parameter *param) session_opts->session_lock_timeout =3D (long)result.uint_32 * HZ; break; =20 + case Opt_ndentrytmo: + session_opts->ndentry_timeout =3D result.int_32; + break; + /* Options for client */ case Opt_msize: if (result.uint_32 < 4096) { --=20 2.50.1 From nobody Sat Feb 7 08:43:21 2026 Received: from e2i340.smtp2go.com (e2i340.smtp2go.com [103.2.141.84]) (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 28C6C3DA7DD for ; Wed, 21 Jan 2026 20:14:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.2.141.84 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769026443; cv=none; b=PcjKDyTeLa6ASng+jWTuhaBPrAJue00wCQNbjQvxbsc4beuzncITvGAOLbjDn/a21t67RAr2LBzbbLct8swforfF1d+hXDV79KhBZP5R53K3vCmJxFFHpuGmcb7nRJPsrn+wxJ8vAPJWLGtjXwCcZZRXDgVjepsP39v5rgeeWo4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769026443; c=relaxed/simple; bh=oXRQ+OIvRVA8lOdQTnWadsO0WEXGTxyNaHE7RJnSbuI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MHqrAB2QMHlmCrCvlcKeeqDaxr6RUZwzqXQDBPDNZObsln1hxQVbObzi/t8UlWfTkl9Fqg75HbXAr/prJegD3XPWFW3au5ki0un7CNya65yneUB+SCuRtAW6m0wjIgWD4n1x03s4dUiqOOSvC08nLHpheMDSIxebACXcyGblltE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=triplefau.lt; spf=pass smtp.mailfrom=em510616.triplefau.lt; dkim=fail (0-bit key) header.d=smtpservice.net header.i=@smtpservice.net header.b=Y1hxpmHG reason="unknown key version"; dkim=pass (2048-bit key) header.d=triplefau.lt header.i=@triplefau.lt header.b=l1KNJIvC; arc=none smtp.client-ip=103.2.141.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=triplefau.lt Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=em510616.triplefau.lt Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="unknown key version" (0-bit key) header.d=smtpservice.net header.i=@smtpservice.net header.b="Y1hxpmHG"; dkim=pass (2048-bit key) header.d=triplefau.lt header.i=@triplefau.lt header.b="l1KNJIvC" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=smtpservice.net; s=maxzs0.a1-4.dyn; x=1769027341; h=Feedback-ID: X-Smtpcorp-Track:Message-ID:Date:Subject:To:From:Reply-To:Sender: List-Unsubscribe:List-Unsubscribe-Post; bh=1bBRb8rvUmXiBXLsNIGFcB0tqFd9sAVSXXO9H/xLowQ=; b=Y1hxpmHGdVpXWVbisydEbIFCwy KUwFRTUpGC8umzFtf6sz5gb8NhTd6XXbg3qtYV6uDqSy5vVkxjeVIuIrrEQaG/z7cnRbxV4J7W580 qi77Wt6PMJl5/Sg+tTWXXd5ODLYOv02+pz2ZbIaK8CxXY8KHzQ+l7xQPfs6Iy6rnA8EYwsI2JqQIs t/XigPqKBUI4UVKc6I4L40yNgVSL5yuAqu1XM9aOSiNrJN3Hfr3+rI03+N2nHG6VcNdnChtzbbs22 CRGnWLR4Oen4W2efwftargdoff5EVgpLM+wALx4MtD1K8YHIFHdCvwiejdaU+jA8pOlZdaur0IvlA 2mpIVaFg==; DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=triplefau.lt; i=@triplefau.lt; q=dns/txt; s=s510616; t=1769026441; h=from : subject : to : message-id : date; bh=1bBRb8rvUmXiBXLsNIGFcB0tqFd9sAVSXXO9H/xLowQ=; b=l1KNJIvCpXz5X6Fb/78S5ZynOuMcVGayZKlcE72kvPYq53C/fcnuFbT5/5bbg4iMVhW87 it6egODUdjZQAtp1VGikL0OrO1jJ7Ny6N/65x5nMEUQVq/728T116Bpm8QPC4cqsq2Q1a7Y JERo4TvS51gSMj0Sbv+wC1kY6Gd+R2S13MfhBOUu/SeE+WZu7nNDliBY4+YQ+MYJtCGs9Qd LlIRHuJKSjcJOZmR1LoSB25y+ShGnYrmyolAxs4YAbPWXGP0bMmZ0XEAd5ymKp7w5yHKkH7 GuV3Fdo1jvZWvMvPubU+PTHo0uQPBhFHlQ7M795f/h/eqZKe6SY+Z4Us8icA== Received: from [10.172.233.45] (helo=SmtpCorp) by smtpcorp.com with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.94.2-S2G) (envelope-from ) id 1vieaM-TRk2I3-Ta; Wed, 21 Jan 2026 20:13:50 +0000 Received: from [10.12.239.196] (helo=localhost) by smtpcorp.com with esmtpsa (TLS1.3:ECDHE_SECP256R1__RSA_PSS_RSAE_SHA256__AES_256_GCM:256) (Exim 4.98.1-S2G) (envelope-from ) id 1vieaM-AIkwcC8tEoU-IKfM; Wed, 21 Jan 2026 20:13:50 +0000 From: Remi Pommarel To: v9fs@lists.linux.dev Cc: linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Van Hensbergen , Latchesar Ionkov , Dominique Martinet , Christian Schoenebeck , Remi Pommarel Subject: [PATCH v2 3/3] 9p: Enable symlink caching in page cache Date: Wed, 21 Jan 2026 20:56:10 +0100 Message-ID: X-Mailer: git-send-email 2.50.1 In-Reply-To: References: 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 X-Smtpcorp-Track: tl-p2at0v3Wy.aoUkW_VmLTPo.QTc-swmgF0f Feedback-ID: 510616m:510616apGKSTK:510616sgw8awW5O5 X-Report-Abuse: Please forward a copy of this message, including all headers, to Content-Type: text/plain; charset="utf-8" Currently, when cache=3Dloose is enabled, file reads are cached in the page cache, but symlink reads are not. This patch allows the results of p9_client_readlink() to be stored in the page cache, eliminating the need for repeated 9P transactions on subsequent symlink accesses. This change improves performance for workloads that involve frequent symlink resolution. Signed-off-by: Remi Pommarel Tested-by: Christian Schoenebeck --- fs/9p/vfs_addr.c | 24 ++++++++++++-- fs/9p/vfs_inode.c | 6 ++-- fs/9p/vfs_inode_dotl.c | 73 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 90 insertions(+), 13 deletions(-) diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index 862164181bac..ee672abbb02c 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -70,10 +70,19 @@ static void v9fs_issue_read(struct netfs_io_subrequest = *subreq) { struct netfs_io_request *rreq =3D subreq->rreq; struct p9_fid *fid =3D rreq->netfs_priv; + char *target; unsigned long long pos =3D subreq->start + subreq->transferred; - int total, err; - - total =3D p9_client_read(fid, pos, &subreq->io_iter, &err); + int total, err, len, n; + + if (S_ISLNK(rreq->inode->i_mode)) { + err =3D p9_client_readlink(fid, &target); + len =3D strnlen(target, PAGE_SIZE - 1); + n =3D copy_to_iter(target, len, &subreq->io_iter); + if (n !=3D len) + err =3D -EFAULT; + total =3D i_size_read(rreq->inode); + } else + total =3D p9_client_read(fid, pos, &subreq->io_iter, &err); =20 /* if we just extended the file size, any portion not in * cache won't be on server and is zeroes */ @@ -99,6 +108,7 @@ static void v9fs_issue_read(struct netfs_io_subrequest *= subreq) static int v9fs_init_request(struct netfs_io_request *rreq, struct file *f= ile) { struct p9_fid *fid; + struct dentry *dentry; bool writing =3D (rreq->origin =3D=3D NETFS_READ_FOR_WRITE || rreq->origin =3D=3D NETFS_WRITETHROUGH || rreq->origin =3D=3D NETFS_UNBUFFERED_WRITE || @@ -115,6 +125,14 @@ static int v9fs_init_request(struct netfs_io_request *= rreq, struct file *file) if (!fid) goto no_fid; p9_fid_get(fid); + } else if (S_ISLNK(rreq->inode->i_mode)) { + dentry =3D d_find_alias(rreq->inode); + if (!dentry) + goto no_fid; + fid =3D v9fs_fid_lookup(dentry); + dput(dentry); + if (IS_ERR(fid)) + goto no_fid; } else { fid =3D v9fs_fid_find_inode(rreq->inode, writing, INVALID_UID, true); if (!fid) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index a82a71be309b..e1b762f3e081 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -302,10 +302,12 @@ int v9fs_init_inode(struct v9fs_session_info *v9ses, goto error; } =20 - if (v9fs_proto_dotl(v9ses)) + if (v9fs_proto_dotl(v9ses)) { inode->i_op =3D &v9fs_symlink_inode_operations_dotl; - else + inode_nohighmem(inode); + } else { inode->i_op =3D &v9fs_symlink_inode_operations; + } =20 break; case S_IFDIR: diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 6312b3590f74..486b11dbada3 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -686,9 +686,13 @@ v9fs_vfs_symlink_dotl(struct mnt_idmap *idmap, struct = inode *dir, int err; kgid_t gid; const unsigned char *name; + umode_t mode; + struct v9fs_session_info *v9ses; struct p9_qid qid; struct p9_fid *dfid; struct p9_fid *fid =3D NULL; + struct inode *inode; + struct posix_acl *dacl =3D NULL, *pacl =3D NULL; =20 name =3D dentry->d_name.name; p9_debug(P9_DEBUG_VFS, "%lu,%s,%s\n", dir->i_ino, name, symname); @@ -702,6 +706,15 @@ v9fs_vfs_symlink_dotl(struct mnt_idmap *idmap, struct = inode *dir, =20 gid =3D v9fs_get_fsgid_for_create(dir); =20 + /* Update mode based on ACL value */ + err =3D v9fs_acl_mode(dir, &mode, &dacl, &pacl); + if (err) { + p9_debug(P9_DEBUG_VFS, + "Failed to get acl values in symlink %d\n", + err); + goto error; + } + /* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */ err =3D p9_client_symlink(dfid, name, symname, gid, &qid); =20 @@ -712,8 +725,30 @@ v9fs_vfs_symlink_dotl(struct mnt_idmap *idmap, struct = inode *dir, =20 v9fs_invalidate_inode_attr(dir); =20 + /* instantiate inode and assign the unopened fid to the dentry */ + fid =3D p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + err =3D PTR_ERR(fid); + p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", + err); + goto error; + } + + v9ses =3D v9fs_inode2v9ses(dir); + inode =3D v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + err =3D PTR_ERR(inode); + p9_debug(P9_DEBUG_VFS, "inode creation failed %d\n", + err); + goto error; + } + v9fs_set_create_acl(inode, fid, dacl, pacl); + v9fs_fid_add(dentry, &fid); + d_instantiate(dentry, inode); + err =3D 0; error: p9_fid_put(fid); + v9fs_put_acl(dacl, pacl); p9_fid_put(dfid); return err; } @@ -853,24 +888,23 @@ v9fs_vfs_mknod_dotl(struct mnt_idmap *idmap, struct i= node *dir, } =20 /** - * v9fs_vfs_get_link_dotl - follow a symlink path + * v9fs_vfs_get_link_nocache_dotl - Resolve a symlink directly. + * + * To be used when symlink caching is not enabled. + * * @dentry: dentry for symlink * @inode: inode for symlink * @done: destructor for return value */ - static const char * -v9fs_vfs_get_link_dotl(struct dentry *dentry, - struct inode *inode, - struct delayed_call *done) +v9fs_vfs_get_link_nocache_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) { struct p9_fid *fid; char *target; int retval; =20 - if (!dentry) - return ERR_PTR(-ECHILD); - p9_debug(P9_DEBUG_VFS, "%pd\n", dentry); =20 fid =3D v9fs_fid_lookup(dentry); @@ -884,6 +918,29 @@ v9fs_vfs_get_link_dotl(struct dentry *dentry, return target; } =20 +/** + * v9fs_vfs_get_link_dotl - follow a symlink path + * @dentry: dentry for symlink + * @inode: inode for symlink + * @done: destructor for return value + */ +static const char * +v9fs_vfs_get_link_dotl(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) +{ + struct v9fs_session_info *v9ses; + + if (!dentry) + return ERR_PTR(-ECHILD); + + v9ses =3D v9fs_inode2v9ses(inode); + if (v9ses->cache & (CACHE_META|CACHE_LOOSE)) + return page_get_link(dentry, inode, done); + + return v9fs_vfs_get_link_nocache_dotl(dentry, inode, done); +} + int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode) { struct p9_stat_dotl *st; --=20 2.50.1