Merge branch 'xfs-4.7-optimise-inline-symlinks' into for-next

This commit is contained in:
Dave Chinner 2016-05-20 10:32:10 +10:00
commit 5b9113547f
10 changed files with 125 additions and 85 deletions

View File

@ -4515,7 +4515,6 @@ int readlink_copy(char __user *buffer, int buflen, const char *link)
out: out:
return len; return len;
} }
EXPORT_SYMBOL(readlink_copy);
/* /*
* A helper for ->readlink(). This should be used *ONLY* for symlinks that * A helper for ->readlink(). This should be used *ONLY* for symlinks that

View File

@ -257,15 +257,12 @@ xfs_dir2_block_to_sf(
* *
* Convert the inode to local format and copy the data in. * Convert the inode to local format and copy the data in.
*/ */
dp->i_df.if_flags &= ~XFS_IFEXTENTS;
dp->i_df.if_flags |= XFS_IFINLINE;
dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
ASSERT(dp->i_df.if_bytes == 0); ASSERT(dp->i_df.if_bytes == 0);
xfs_idata_realloc(dp, size, XFS_DATA_FORK); xfs_init_local_fork(dp, XFS_DATA_FORK, dst, size);
dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
dp->i_d.di_size = size;
logflags |= XFS_ILOG_DDATA; logflags |= XFS_ILOG_DDATA;
memcpy(dp->i_df.if_u1.if_data, dst, size);
dp->i_d.di_size = size;
xfs_dir2_sf_check(args); xfs_dir2_sf_check(args);
out: out:
xfs_trans_log_inode(args->trans, dp, logflags); xfs_trans_log_inode(args->trans, dp, logflags);

View File

@ -231,6 +231,48 @@ xfs_iformat_fork(
return error; return error;
} }
void
xfs_init_local_fork(
struct xfs_inode *ip,
int whichfork,
const void *data,
int size)
{
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
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 (mem_size <= sizeof(ifp->if_u2.if_inline_data))
ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
else {
real_size = roundup(mem_size, 4);
ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
}
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;
ifp->if_flags &= ~(XFS_IFEXTENTS | XFS_IFBROOT);
ifp->if_flags |= XFS_IFINLINE;
}
/* /*
* The file is in-lined in the on-disk inode. * The file is in-lined in the on-disk inode.
* If it fits into if_inline_data, then copy * If it fits into if_inline_data, then copy
@ -248,8 +290,6 @@ xfs_iformat_local(
int whichfork, int whichfork,
int size) int size)
{ {
xfs_ifork_t *ifp;
int real_size;
/* /*
* If the size is unreasonable, then something * If the size is unreasonable, then something
@ -265,22 +305,8 @@ xfs_iformat_local(
ip->i_mount, dip); ip->i_mount, dip);
return -EFSCORRUPTED; return -EFSCORRUPTED;
} }
ifp = XFS_IFORK_PTR(ip, whichfork);
real_size = 0; xfs_init_local_fork(ip, whichfork, XFS_DFORK_PTR(dip, whichfork), size);
if (size == 0)
ifp->if_u1.if_data = NULL;
else if (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);
ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
}
ifp->if_bytes = size;
ifp->if_real_bytes = real_size;
if (size)
memcpy(ifp->if_u1.if_data, XFS_DFORK_PTR(dip, whichfork), size);
ifp->if_flags &= ~XFS_IFEXTENTS;
ifp->if_flags |= XFS_IFINLINE;
return 0; return 0;
} }

View File

@ -134,6 +134,7 @@ void xfs_iroot_realloc(struct xfs_inode *, int, int);
int xfs_iread_extents(struct xfs_trans *, struct xfs_inode *, int); int xfs_iread_extents(struct xfs_trans *, struct xfs_inode *, int);
int xfs_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *, int xfs_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *,
int); int);
void xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
struct xfs_bmbt_rec_host * struct xfs_bmbt_rec_host *
xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t); xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);

View File

@ -2840,6 +2840,7 @@ xfs_rename_alloc_whiteout(
* and flag it as linkable. * and flag it as linkable.
*/ */
drop_nlink(VFS_I(tmpfile)); drop_nlink(VFS_I(tmpfile));
xfs_setup_iops(tmpfile);
xfs_finish_inode_setup(tmpfile); xfs_finish_inode_setup(tmpfile);
VFS_I(tmpfile)->i_state |= I_LINKABLE; VFS_I(tmpfile)->i_state |= I_LINKABLE;

View File

@ -440,6 +440,9 @@ loff_t __xfs_seek_hole_data(struct inode *inode, loff_t start,
/* from xfs_iops.c */ /* from xfs_iops.c */
extern void xfs_setup_inode(struct xfs_inode *ip);
extern void xfs_setup_iops(struct xfs_inode *ip);
/* /*
* When setting up a newly allocated inode, we need to call * When setting up a newly allocated inode, we need to call
* xfs_finish_inode_setup() once the inode is fully instantiated at * xfs_finish_inode_setup() once the inode is fully instantiated at
@ -447,7 +450,6 @@ loff_t __xfs_seek_hole_data(struct inode *inode, loff_t start,
* before we've completed instantiation. Otherwise we can do it * before we've completed instantiation. Otherwise we can do it
* the moment the inode lookup is complete. * the moment the inode lookup is complete.
*/ */
extern void xfs_setup_inode(struct xfs_inode *ip);
static inline void xfs_finish_inode_setup(struct xfs_inode *ip) static inline void xfs_finish_inode_setup(struct xfs_inode *ip)
{ {
xfs_iflags_clear(ip, XFS_INEW); xfs_iflags_clear(ip, XFS_INEW);
@ -458,6 +460,7 @@ static inline void xfs_finish_inode_setup(struct xfs_inode *ip)
static inline void xfs_setup_existing_inode(struct xfs_inode *ip) static inline void xfs_setup_existing_inode(struct xfs_inode *ip)
{ {
xfs_setup_inode(ip); xfs_setup_inode(ip);
xfs_setup_iops(ip);
xfs_finish_inode_setup(ip); xfs_finish_inode_setup(ip);
} }

View File

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

View File

@ -277,7 +277,6 @@ xfs_readlink_by_handle(
{ {
struct dentry *dentry; struct dentry *dentry;
__u32 olen; __u32 olen;
void *link;
int error; int error;
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
@ -288,7 +287,7 @@ xfs_readlink_by_handle(
return PTR_ERR(dentry); return PTR_ERR(dentry);
/* Restrict this handle operation to symlinks only. */ /* Restrict this handle operation to symlinks only. */
if (!d_is_symlink(dentry)) { if (!d_inode(dentry)->i_op->readlink) {
error = -EINVAL; error = -EINVAL;
goto out_dput; goto out_dput;
} }
@ -298,21 +297,8 @@ xfs_readlink_by_handle(
goto out_dput; goto out_dput;
} }
link = kmalloc(MAXPATHLEN+1, GFP_KERNEL); error = d_inode(dentry)->i_op->readlink(dentry, hreq->ohandle, olen);
if (!link) {
error = -ENOMEM;
goto out_dput;
}
error = xfs_readlink(XFS_I(d_inode(dentry)), link);
if (error)
goto out_kfree;
error = readlink_copy(hreq->ohandle, olen, link);
if (error)
goto out_kfree;
out_kfree:
kfree(link);
out_dput: out_dput:
dput(dentry); dput(dentry);
return error; return error;

View File

@ -181,6 +181,8 @@ xfs_generic_create(
} }
#endif #endif
xfs_setup_iops(ip);
if (tmpfile) if (tmpfile)
d_tmpfile(dentry, inode); d_tmpfile(dentry, inode);
else else
@ -368,6 +370,8 @@ xfs_vn_symlink(
if (unlikely(error)) if (unlikely(error))
goto out_cleanup_inode; goto out_cleanup_inode;
xfs_setup_iops(cip);
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
xfs_finish_inode_setup(cip); xfs_finish_inode_setup(cip);
return 0; return 0;
@ -442,6 +446,16 @@ xfs_vn_get_link(
return ERR_PTR(error); 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 STATIC int
xfs_vn_getattr( xfs_vn_getattr(
struct vfsmount *mnt, struct vfsmount *mnt,
@ -1160,6 +1174,18 @@ static const struct inode_operations xfs_symlink_inode_operations = {
.update_time = xfs_vn_update_time, .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 STATIC void
xfs_diflags_to_iflags( xfs_diflags_to_iflags(
struct inode *inode, struct inode *inode,
@ -1186,7 +1212,7 @@ xfs_diflags_to_iflags(
} }
/* /*
* Initialize the Linux inode and set up the operation vectors. * Initialize the Linux inode.
* *
* When reading existing inodes from disk this is called directly from xfs_iget, * When reading existing inodes from disk this is called directly from xfs_iget,
* when creating a new inode it is called from xfs_ialloc after setting up the * when creating a new inode it is called from xfs_ialloc after setting up the
@ -1225,32 +1251,12 @@ xfs_setup_inode(
i_size_write(inode, ip->i_d.di_size); i_size_write(inode, ip->i_d.di_size);
xfs_diflags_to_iflags(inode, ip); xfs_diflags_to_iflags(inode, ip);
ip->d_ops = ip->i_mount->m_nondir_inode_ops; if (S_ISDIR(inode->i_mode)) {
lockdep_set_class(&ip->i_lock.mr_lock, &xfs_nondir_ilock_class);
switch (inode->i_mode & S_IFMT) {
case S_IFREG:
inode->i_op = &xfs_inode_operations;
inode->i_fop = &xfs_file_operations;
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
case S_IFDIR:
lockdep_set_class(&ip->i_lock.mr_lock, &xfs_dir_ilock_class); lockdep_set_class(&ip->i_lock.mr_lock, &xfs_dir_ilock_class);
if (xfs_sb_version_hasasciici(&XFS_M(inode->i_sb)->m_sb))
inode->i_op = &xfs_dir_ci_inode_operations;
else
inode->i_op = &xfs_dir_inode_operations;
inode->i_fop = &xfs_dir_file_operations;
ip->d_ops = ip->i_mount->m_dir_inode_ops; ip->d_ops = ip->i_mount->m_dir_inode_ops;
break; } else {
case S_IFLNK: ip->d_ops = ip->i_mount->m_nondir_inode_ops;
inode->i_op = &xfs_symlink_inode_operations; lockdep_set_class(&ip->i_lock.mr_lock, &xfs_nondir_ilock_class);
if (!(ip->i_df.if_flags & XFS_IFINLINE))
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
default:
inode->i_op = &xfs_inode_operations;
init_special_inode(inode, inode->i_mode, inode->i_rdev);
break;
} }
/* /*
@ -1270,3 +1276,35 @@ xfs_setup_inode(
cache_no_acl(inode); cache_no_acl(inode);
} }
} }
void
xfs_setup_iops(
struct xfs_inode *ip)
{
struct inode *inode = &ip->i_vnode;
switch (inode->i_mode & S_IFMT) {
case S_IFREG:
inode->i_op = &xfs_inode_operations;
inode->i_fop = &xfs_file_operations;
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
case S_IFDIR:
if (xfs_sb_version_hasasciici(&XFS_M(inode->i_sb)->m_sb))
inode->i_op = &xfs_dir_ci_inode_operations;
else
inode->i_op = &xfs_dir_inode_operations;
inode->i_fop = &xfs_dir_file_operations;
break;
case S_IFLNK:
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;
init_special_inode(inode, inode->i_mode, inode->i_rdev);
break;
}
}

View File

@ -131,6 +131,8 @@ xfs_readlink(
trace_xfs_readlink(ip); trace_xfs_readlink(ip);
ASSERT(!(ip->i_df.if_flags & XFS_IFINLINE));
if (XFS_FORCED_SHUTDOWN(mp)) if (XFS_FORCED_SHUTDOWN(mp))
return -EIO; return -EIO;
@ -150,12 +152,7 @@ xfs_readlink(
} }
if (ip->i_df.if_flags & XFS_IFINLINE) { error = xfs_readlink_bmap(ip, link);
memcpy(link, ip->i_df.if_u1.if_data, pathlen);
link[pathlen] = '\0';
} else {
error = xfs_readlink_bmap(ip, link);
}
out: out:
xfs_iunlock(ip, XFS_ILOCK_SHARED); xfs_iunlock(ip, XFS_ILOCK_SHARED);
@ -303,19 +300,11 @@ xfs_symlink(
* If the symlink will fit into the inode, write it inline. * If the symlink will fit into the inode, write it inline.
*/ */
if (pathlen <= XFS_IFORK_DSIZE(ip)) { if (pathlen <= XFS_IFORK_DSIZE(ip)) {
xfs_idata_realloc(ip, pathlen, XFS_DATA_FORK); xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, pathlen);
memcpy(ip->i_df.if_u1.if_data, target_path, pathlen);
ip->i_d.di_size = pathlen; ip->i_d.di_size = pathlen;
/*
* The inode was initially created in extent format.
*/
ip->i_df.if_flags &= ~(XFS_IFEXTENTS | XFS_IFBROOT);
ip->i_df.if_flags |= XFS_IFINLINE;
ip->i_d.di_format = XFS_DINODE_FMT_LOCAL; ip->i_d.di_format = XFS_DINODE_FMT_LOCAL;
xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE); xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
} else { } else {
int offset; int offset;