add get_name implementation for exports. (#16833)

This fixes a serious performance problem with NFS handling of large
directories, as the new get_name code is much more efficient than the
default zfs_readdir. This is actually part of
20232ecfaa in 2.3. But I've taken only
the minimum code to implement get_name, and not the rest of the long
name changes.

Signed-off-by: Charles Hedrick <hedrick@rutgers.edu>
Co-authored-by: Charles L. Hedrick <hedrick@ncommunis.cs.rutgers.edu>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
This commit is contained in:
Charles Hedrick 2024-12-04 17:01:57 -05:00 committed by Tony Hutter
parent 299da6ace3
commit 0bd8481aa7
3 changed files with 75 additions and 0 deletions

View File

@ -44,6 +44,7 @@ extern int zfs_write_simple(znode_t *zp, const void *data, size_t len,
loff_t pos, size_t *resid); loff_t pos, size_t *resid);
extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, int flags, extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, int flags,
cred_t *cr, int *direntflags, pathname_t *realpnp); cred_t *cr, int *direntflags, pathname_t *realpnp);
extern int zfs_get_name(znode_t *dzp, char *name, znode_t *zp);
extern int zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl, extern int zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp, int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp,
zidmap_t *mnt_ns); zidmap_t *mnt_ns);

View File

@ -526,6 +526,48 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr,
return (error); return (error);
} }
/*
* Perform a linear search in directory for the name of specific inode.
* Note we don't pass in the buffer size of name because it's hardcoded to
* NAME_MAX+1(256) in Linux.
*
* IN: dzp - znode of directory to search.
* zp - znode of the target
*
* OUT: name - dentry name of the target
*
* RETURN: 0 on success, error code on failure.
*/
int
zfs_get_name(znode_t *dzp, char *name, znode_t *zp)
{
zfsvfs_t *zfsvfs = ZTOZSB(dzp);
int error = 0;
if ((error = zfs_enter_verify_zp(zfsvfs, dzp, FTAG)) != 0)
return (error);
if ((error = zfs_verify_zp(zp)) != 0) {
zfs_exit(zfsvfs, FTAG);
return (error);
}
/* ctldir should have got their name in zfs_vget */
if (dzp->z_is_ctldir || zp->z_is_ctldir) {
zfs_exit(zfsvfs, FTAG);
return (ENOENT);
}
/* buffer len is hardcoded to 256 in Linux kernel */
error = zap_value_search(zfsvfs->z_os, dzp->z_id, zp->z_id,
ZFS_DIRENT_OBJ(-1ULL), name);
zfs_exit(zfsvfs, FTAG);
return (error);
}
/* /*
* Attempt to create a new entry in a directory. If the entry * Attempt to create a new entry in a directory. If the entry
* already exists, truncate the file if permissible, else return * already exists, truncate the file if permissible, else return

View File

@ -24,6 +24,7 @@
*/ */
#include <sys/file.h>
#include <sys/zfs_znode.h> #include <sys/zfs_znode.h>
#include <sys/zfs_vnops.h> #include <sys/zfs_vnops.h>
#include <sys/zfs_ctldir.h> #include <sys/zfs_ctldir.h>
@ -102,6 +103,36 @@ zpl_fh_to_dentry(struct super_block *sb, struct fid *fh,
return (d_obtain_alias(ip)); return (d_obtain_alias(ip));
} }
/*
* In case the filesystem contains name longer than 255, we need to override
* the default get_name so we don't get buffer overflow. Unfortunately, since
* the buffer size is hardcoded in Linux, we will get ESTALE error in this
* case.
*/
static int
zpl_get_name(struct dentry *parent, char *name, struct dentry *child)
{
cred_t *cr = CRED();
fstrans_cookie_t cookie;
struct inode *dir = parent->d_inode;
struct inode *ip = child->d_inode;
int error;
if (!dir || !S_ISDIR(dir->i_mode))
return (-ENOTDIR);
crhold(cr);
cookie = spl_fstrans_mark();
spl_inode_lock_shared(dir);
error = -zfs_get_name(ITOZ(dir), name, ITOZ(ip));
spl_inode_unlock_shared(dir);
spl_fstrans_unmark(cookie);
crfree(cr);
return (error);
}
static struct dentry * static struct dentry *
zpl_get_parent(struct dentry *child) zpl_get_parent(struct dentry *child)
{ {
@ -146,6 +177,7 @@ zpl_commit_metadata(struct inode *inode)
const struct export_operations zpl_export_operations = { const struct export_operations zpl_export_operations = {
.encode_fh = zpl_encode_fh, .encode_fh = zpl_encode_fh,
.fh_to_dentry = zpl_fh_to_dentry, .fh_to_dentry = zpl_fh_to_dentry,
.get_name = zpl_get_name,
.get_parent = zpl_get_parent, .get_parent = zpl_get_parent,
.commit_metadata = zpl_commit_metadata, .commit_metadata = zpl_commit_metadata,
}; };