mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
FreeBSD: avoid lookup overhead for nonexistent xattr directories
Port the z_xattr_dir_absent cache to FreeBSD. As on Linux, a getextattr that misses in the SA otherwise falls through to zfs_get_xattrdir(), which takes the "" ZXATTR dirlock and reads SA_ZPL_XATTR only to find the file has no xattr directory. The in-core znode now caches that: after an SA miss zfs_getextattr_impl() skips the directory lookup when the flag is set, zfs_get_xattrdir() sets it when no directory is found and clears it when one is found, and zfs_make_xattrdir() clears it on creation, which also covers the TX_MKXATTR ZIL replay path. The flag is serialized by the base file's vnode lock. zfs_make_xattrdir(), the only path that creates the directory and clears the flag, runs with the vnode held exclusive, while every reader that sets the flag holds it shared, so a set can never race the clear. ASSERT_VOP_ELOCKED() in zfs_make_xattrdir() and ASSERT_VOP_LOCKED() in zfs_get_xattrdir() enforce this, both skipped during ZIL replay since it is single threaded with no locked vnode. There is no on-disk format change. Reviewed-by: Alexander Motin <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Reviewed-by: Richard Yao <[email protected]> Reviewed-by: Rob Norris <[email protected]> Signed-off-by: Ameer Hamza <[email protected]> Closes #18687
This commit is contained in:
1 parent
04092e815e
commit
2ea519c2a5
3 files changed
+32
-2
No files matched your search
@@ -828,6 +828,9 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xvpp, cred_t *cr)
|
||||
boolean_t fuid_dirtied;
|
||||
uint64_t parent __maybe_unused;
|
||||
|
||||
if (zfsvfs->z_replay == B_FALSE)
|
||||
ASSERT_VOP_ELOCKED(ZTOV(zp), __func__);
|
||||
|
||||
*xvpp = NULL;
|
||||
|
||||
if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
|
||||
@@ -875,6 +878,9 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, znode_t **xvpp, cred_t *cr)
|
||||
|
||||
getnewvnode_drop_reserve();
|
||||
|
||||
/* Record that the file now has an xattr directory. */
|
||||
zp->z_xattr_dir_absent = B_FALSE;
|
||||
|
||||
*xvpp = xzp;
|
||||
|
||||
return (0);
|
||||
@@ -900,6 +906,19 @@ zfs_get_xattrdir(znode_t *zp, znode_t **xzpp, cred_t *cr, int flags)
|
||||
znode_t *xzp;
|
||||
vattr_t va;
|
||||
int error;
|
||||
|
||||
if (zfsvfs->z_replay == B_FALSE)
|
||||
ASSERT_VOP_LOCKED(ZTOV(zp), __func__);
|
||||
|
||||
/*
|
||||
* Fast path: a file already known to have no xattr directory, when not
|
||||
* creating one, returns without taking the "" ZXATTR dirlock or doing
|
||||
* the SA_ZPL_XATTR lookup below. z_xattr_dir_absent tracks this: it is
|
||||
* set when the lookup finds no directory and cleared when one is found
|
||||
* or created.
|
||||
*/
|
||||
if (!(flags & CREATE_XATTR_DIR) && zp->z_xattr_dir_absent)
|
||||
return (SET_ERROR(ENOATTR));
|
||||
top:
|
||||
error = zfs_dirent_lookup(zp, "", &xzp, ZXATTR);
|
||||
if (error)
|
||||
@@ -907,12 +926,19 @@ top:
|
||||
|
||||
if (xzp != NULL) {
|
||||
*xzpp = xzp;
|
||||
zp->z_xattr_dir_absent = B_FALSE;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
if (!(flags & CREATE_XATTR_DIR))
|
||||
if (!(flags & CREATE_XATTR_DIR)) {
|
||||
/*
|
||||
* z_xattr_dir_absent is serialized by the base vnode lock,
|
||||
* which the creator holds exclusive and readers hold shared.
|
||||
*/
|
||||
zp->z_xattr_dir_absent = B_TRUE;
|
||||
return (SET_ERROR(ENOATTR));
|
||||
}
|
||||
|
||||
if (zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) {
|
||||
return (SET_ERROR(EROFS));
|
||||
|
||||
@@ -6021,7 +6021,7 @@ zfs_getextattr_impl(struct vop_getextattr_args *ap, boolean_t compat)
|
||||
error = ENOENT;
|
||||
if (zfsvfs->z_use_sa && zp->z_is_sa)
|
||||
error = zfs_getextattr_sa(ap, attrname);
|
||||
if (error == ENOENT)
|
||||
if (error == ENOENT && !zp->z_xattr_dir_absent)
|
||||
error = zfs_getextattr_dir(ap, attrname);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@@ -287,6 +287,7 @@ zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
|
||||
ASSERT(!POINTER_IS_VALID(sharezp->z_zfsvfs));
|
||||
sharezp->z_unlinked = 0;
|
||||
sharezp->z_atime_dirty = 0;
|
||||
sharezp->z_xattr_dir_absent = B_FALSE;
|
||||
sharezp->z_zfsvfs = zfsvfs;
|
||||
sharezp->z_is_sa = zfsvfs->z_use_sa;
|
||||
sharezp->z_pflags = 0;
|
||||
@@ -447,6 +448,7 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz,
|
||||
zp->z_sa_hdl = NULL;
|
||||
zp->z_unlinked = 0;
|
||||
zp->z_atime_dirty = 0;
|
||||
zp->z_xattr_dir_absent = B_FALSE;
|
||||
zp->z_mapcnt = 0;
|
||||
zp->z_id = db->db_object;
|
||||
zp->z_blksz = blksz;
|
||||
@@ -1130,6 +1132,7 @@ zfs_rezget(znode_t *zp)
|
||||
nvlist_free(zp->z_xattr_cached);
|
||||
zp->z_xattr_cached = NULL;
|
||||
}
|
||||
zp->z_xattr_dir_absent = B_FALSE;
|
||||
rw_exit(&zp->z_xattr_lock);
|
||||
|
||||
ASSERT0P(zp->z_sa_hdl);
|
||||
@@ -1810,6 +1813,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
|
||||
ASSERT(!POINTER_IS_VALID(rootzp->z_zfsvfs));
|
||||
rootzp->z_unlinked = 0;
|
||||
rootzp->z_atime_dirty = 0;
|
||||
rootzp->z_xattr_dir_absent = B_FALSE;
|
||||
rootzp->z_is_sa = USE_SA(version, os);
|
||||
rootzp->z_pflags = 0;
|
||||
|
||||
|
||||
Reference in new issue
Block a user