xfs: optimize inline symlinks

By overallocating the in-core inode fork data buffer and zero
terminating the link target in xfs_init_local_fork we can avoid
the memory allocation in ->follow_link.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Christoph Hellwig 2016-04-06 07:53:29 +10:00 committed by Dave Chinner
parent bfe8804d90
commit 30ee052e12
4 changed files with 49 additions and 15 deletions

View file

@ -239,19 +239,33 @@ xfs_init_local_fork(
int size)
{
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
int real_size = 0;
int mem_size = size, real_size = 0;
bool zero_terminate;
/*
* If we are using the local fork to store a symlink body we need to
* zero-terminate it so that we can pass it back to the VFS directly.
* Overallocate the in-memory fork by one for that and add a zero
* to terminate it below.
*/
zero_terminate = S_ISLNK(VFS_I(ip)->i_mode);
if (zero_terminate)
mem_size++;
if (size == 0)
ifp->if_u1.if_data = NULL;
else if (size <= sizeof(ifp->if_u2.if_inline_data))
else if (mem_size <= sizeof(ifp->if_u2.if_inline_data))
ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
else {
real_size = roundup(size, 4);
real_size = roundup(mem_size, 4);
ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
}
if (size)
if (size) {
memcpy(ifp->if_u1.if_data, data, size);
if (zero_terminate)
ifp->if_u1.if_data[size] = '\0';
}
ifp->if_bytes = size;
ifp->if_real_bytes = real_size;

View file

@ -210,7 +210,7 @@ xfs_inode_item_format_data_fork(
*/
data_bytes = roundup(ip->i_df.if_bytes, 4);
ASSERT(ip->i_df.if_real_bytes == 0 ||
ip->i_df.if_real_bytes == data_bytes);
ip->i_df.if_real_bytes >= data_bytes);
ASSERT(ip->i_df.if_u1.if_data != NULL);
ASSERT(ip->i_d.di_size > 0);
xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_ILOCAL,
@ -305,7 +305,7 @@ xfs_inode_item_format_attr_fork(
*/
data_bytes = roundup(ip->i_afp->if_bytes, 4);
ASSERT(ip->i_afp->if_real_bytes == 0 ||
ip->i_afp->if_real_bytes == data_bytes);
ip->i_afp->if_real_bytes >= data_bytes);
ASSERT(ip->i_afp->if_u1.if_data != NULL);
xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL,
ip->i_afp->if_u1.if_data,

View file

@ -446,6 +446,16 @@ xfs_vn_get_link(
return ERR_PTR(error);
}
STATIC const char *
xfs_vn_get_link_inline(
struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
{
ASSERT(XFS_I(inode)->i_df.if_flags & XFS_IFINLINE);
return XFS_I(inode)->i_df.if_u1.if_data;
}
STATIC int
xfs_vn_getattr(
struct vfsmount *mnt,
@ -1171,6 +1181,18 @@ static const struct inode_operations xfs_symlink_inode_operations = {
.update_time = xfs_vn_update_time,
};
static const struct inode_operations xfs_inline_symlink_inode_operations = {
.readlink = generic_readlink,
.get_link = xfs_vn_get_link_inline,
.getattr = xfs_vn_getattr,
.setattr = xfs_vn_setattr,
.setxattr = generic_setxattr,
.getxattr = generic_getxattr,
.removexattr = generic_removexattr,
.listxattr = xfs_vn_listxattr,
.update_time = xfs_vn_update_time,
};
STATIC void
xfs_diflags_to_iflags(
struct inode *inode,
@ -1282,9 +1304,10 @@ xfs_setup_iops(
inode->i_fop = &xfs_dir_file_operations;
break;
case S_IFLNK:
inode->i_op = &xfs_symlink_inode_operations;
if (!(ip->i_df.if_flags & XFS_IFINLINE))
inode->i_mapping->a_ops = &xfs_address_space_operations;
if (ip->i_df.if_flags & XFS_IFINLINE)
inode->i_op = &xfs_inline_symlink_inode_operations;
else
inode->i_op = &xfs_symlink_inode_operations;
break;
default:
inode->i_op = &xfs_inode_operations;

View file

@ -131,6 +131,8 @@ xfs_readlink(
trace_xfs_readlink(ip);
ASSERT(!(ip->i_df.if_flags & XFS_IFINLINE));
if (XFS_FORCED_SHUTDOWN(mp))
return -EIO;
@ -150,12 +152,7 @@ xfs_readlink(
}
if (ip->i_df.if_flags & XFS_IFINLINE) {
memcpy(link, ip->i_df.if_u1.if_data, pathlen);
link[pathlen] = '\0';
} else {
error = xfs_readlink_bmap(ip, link);
}
error = xfs_readlink_bmap(ip, link);
out:
xfs_iunlock(ip, XFS_ILOCK_SHARED);