aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2026-03-05 12:35:43 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2026-03-10 12:44:34 +0000
commit2b256f00aaee4713b8e6f0e3c0f3493065f710c4 (patch)
treeba63e135948fe64d13dd87c07b428ea9440dfbf7
parent92d7808d88f0de979d76446c76c7324731c41302 (diff)
p9fs: locking improvements for p9fs_stat_vnode_dotl()
If the vnode is share-locked: - Use vn_delayed_setsize() to avoid calling vnode_pager_setsize() with the vnode only shared locked. - Interlock the vnode to get exclusive mode for updating the node fields. Reciprocally, interlock the vnode in p9fs_getattr_dotl() to observe the consistent values on read. PR: 293492 Reviewed by: markj Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D55665
-rw-r--r--sys/fs/p9fs/p9fs_vnops.c42
1 files changed, 39 insertions, 3 deletions
diff --git a/sys/fs/p9fs/p9fs_vnops.c b/sys/fs/p9fs/p9fs_vnops.c
index e64d7840f7f9..c6d35548e9ed 100644
--- a/sys/fs/p9fs/p9fs_vnops.c
+++ b/sys/fs/p9fs/p9fs_vnops.c
@@ -896,6 +896,7 @@ p9fs_getattr_dotl(struct vop_getattr_args *ap)
/* Basic info */
VATTR_NULL(vap);
+ VI_LOCK(vp);
vap->va_atime.tv_sec = inode->i_atime;
vap->va_mtime.tv_sec = inode->i_mtime;
vap->va_ctime.tv_sec = inode->i_ctime;
@@ -916,6 +917,7 @@ p9fs_getattr_dotl(struct vop_getattr_args *ap)
vap->va_filerev = inode->data_version;
vap->va_vaflags = 0;
vap->va_bytes = inode->blocks * P9PROTO_TGETATTR_BLK;
+ VI_UNLOCK(vp);
return (0);
}
@@ -951,16 +953,36 @@ p9fs_stat_vnode_dotl(struct p9_stat_dotl *stat, struct vnode *vp)
{
struct p9fs_node *np;
struct p9fs_inode *inode;
+ bool excl_locked;
np = P9FS_VTON(vp);
inode = &np->inode;
+ /*
+ * This function might be called with the vnode only shared
+ * locked. Then, interlock the vnode to ensure the exclusive
+ * access to the inode fields: the thread either owns
+ * exclusive vnode lock, or shared vnode lock plus interlock.
+ *
+ * If the vnode is locked exclusive, do not take the
+ * interlock. We directly call vnode_pager_setsize(), which
+ * needs the vm_object lock, and that lock is before vnode
+ * interlock in the lock order.
+ */
ASSERT_VOP_LOCKED(vp, __func__);
+ excl_locked = VOP_ISLOCKED(vp) == LK_EXCLUSIVE;
+ if (!excl_locked)
+ VI_LOCK(vp);
+
/* Update the pager size if file size changes on host */
if (inode->i_size != stat->st_size) {
inode->i_size = stat->st_size;
- if (vp->v_type == VREG)
- vnode_pager_setsize(vp, inode->i_size);
+ if (vp->v_type == VREG) {
+ if (excl_locked)
+ vnode_pager_setsize(vp, inode->i_size);
+ else
+ vn_delayed_setsize_locked(vp);
+ }
}
inode->i_mtime = stat->st_mtime_sec;
@@ -979,11 +1001,12 @@ p9fs_stat_vnode_dotl(struct p9_stat_dotl *stat, struct vnode *vp)
inode->gen = stat->st_gen;
inode->data_version = stat->st_data_version;
- ASSERT_VOP_LOCKED(vp, __func__);
/* Setting a flag if file changes based on qid version */
if (np->vqid.qid_version != stat->qid.version)
np->flags |= P9FS_NODE_MODIFIED;
memcpy(&np->vqid, &stat->qid, sizeof(stat->qid));
+ if (!excl_locked)
+ VI_UNLOCK(vp);
return (0);
}
@@ -2213,12 +2236,25 @@ p9fs_putpages(struct vop_putpages_args *ap)
return (rtvals[0]);
}
+static int
+p9fs_delayed_setsize(struct vop_delayed_setsize_args *ap)
+{
+ struct vnode *vp;
+ struct p9fs_node *np;
+
+ vp = ap->a_vp;
+ np = P9FS_VTON(vp);
+ vnode_pager_setsize(vp, np->inode.i_size);
+ return (0);
+}
+
struct vop_vector p9fs_vnops = {
.vop_default = &default_vnodeops,
.vop_lookup = p9fs_lookup,
.vop_open = p9fs_open,
.vop_close = p9fs_close,
.vop_access = p9fs_access,
+ .vop_delayed_setsize = p9fs_delayed_setsize,
.vop_getattr = p9fs_getattr_dotl,
.vop_setattr = p9fs_setattr_dotl,
.vop_reclaim = p9fs_reclaim,