mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-02-05 23:03:30 +03:00
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:
parent
299da6ace3
commit
0bd8481aa7
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user