Merge branch 'xfs-4.7-optimise-inline-symlinks' into for-next
This commit is contained in:
commit
5b9113547f
@ -4515,7 +4515,6 @@ int readlink_copy(char __user *buffer, int buflen, const char *link)
|
||||
out:
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(readlink_copy);
|
||||
|
||||
/*
|
||||
* A helper for ->readlink(). This should be used *ONLY* for symlinks that
|
||||
|
@ -257,15 +257,12 @@ xfs_dir2_block_to_sf(
|
||||
*
|
||||
* 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);
|
||||
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;
|
||||
memcpy(dp->i_df.if_u1.if_data, dst, size);
|
||||
dp->i_d.di_size = size;
|
||||
xfs_dir2_sf_check(args);
|
||||
out:
|
||||
xfs_trans_log_inode(args->trans, dp, logflags);
|
||||
|
@ -231,6 +231,48 @@ xfs_iformat_fork(
|
||||
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.
|
||||
* If it fits into if_inline_data, then copy
|
||||
@ -248,8 +290,6 @@ xfs_iformat_local(
|
||||
int whichfork,
|
||||
int size)
|
||||
{
|
||||
xfs_ifork_t *ifp;
|
||||
int real_size;
|
||||
|
||||
/*
|
||||
* If the size is unreasonable, then something
|
||||
@ -265,22 +305,8 @@ xfs_iformat_local(
|
||||
ip->i_mount, dip);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
ifp = XFS_IFORK_PTR(ip, whichfork);
|
||||
real_size = 0;
|
||||
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;
|
||||
|
||||
xfs_init_local_fork(ip, whichfork, XFS_DFORK_PTR(dip, whichfork), size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *,
|
||||
int);
|
||||
void xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
|
||||
|
||||
struct xfs_bmbt_rec_host *
|
||||
xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
|
||||
|
@ -2840,6 +2840,7 @@ xfs_rename_alloc_whiteout(
|
||||
* and flag it as linkable.
|
||||
*/
|
||||
drop_nlink(VFS_I(tmpfile));
|
||||
xfs_setup_iops(tmpfile);
|
||||
xfs_finish_inode_setup(tmpfile);
|
||||
VFS_I(tmpfile)->i_state |= I_LINKABLE;
|
||||
|
||||
|
@ -440,6 +440,9 @@ loff_t __xfs_seek_hole_data(struct inode *inode, loff_t start,
|
||||
|
||||
|
||||
/* 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
|
||||
* 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
|
||||
* 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
xfs_setup_inode(ip);
|
||||
xfs_setup_iops(ip);
|
||||
xfs_finish_inode_setup(ip);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -277,7 +277,6 @@ xfs_readlink_by_handle(
|
||||
{
|
||||
struct dentry *dentry;
|
||||
__u32 olen;
|
||||
void *link;
|
||||
int error;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
@ -288,7 +287,7 @@ xfs_readlink_by_handle(
|
||||
return PTR_ERR(dentry);
|
||||
|
||||
/* Restrict this handle operation to symlinks only. */
|
||||
if (!d_is_symlink(dentry)) {
|
||||
if (!d_inode(dentry)->i_op->readlink) {
|
||||
error = -EINVAL;
|
||||
goto out_dput;
|
||||
}
|
||||
@ -298,21 +297,8 @@ xfs_readlink_by_handle(
|
||||
goto out_dput;
|
||||
}
|
||||
|
||||
link = kmalloc(MAXPATHLEN+1, GFP_KERNEL);
|
||||
if (!link) {
|
||||
error = -ENOMEM;
|
||||
goto out_dput;
|
||||
}
|
||||
error = d_inode(dentry)->i_op->readlink(dentry, hreq->ohandle, olen);
|
||||
|
||||
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:
|
||||
dput(dentry);
|
||||
return error;
|
||||
|
@ -181,6 +181,8 @@ xfs_generic_create(
|
||||
}
|
||||
#endif
|
||||
|
||||
xfs_setup_iops(ip);
|
||||
|
||||
if (tmpfile)
|
||||
d_tmpfile(dentry, inode);
|
||||
else
|
||||
@ -368,6 +370,8 @@ xfs_vn_symlink(
|
||||
if (unlikely(error))
|
||||
goto out_cleanup_inode;
|
||||
|
||||
xfs_setup_iops(cip);
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
xfs_finish_inode_setup(cip);
|
||||
return 0;
|
||||
@ -442,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,
|
||||
@ -1160,6 +1174,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,
|
||||
@ -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 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);
|
||||
xfs_diflags_to_iflags(inode, ip);
|
||||
|
||||
ip->d_ops = ip->i_mount->m_nondir_inode_ops;
|
||||
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:
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
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;
|
||||
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;
|
||||
break;
|
||||
default:
|
||||
inode->i_op = &xfs_inode_operations;
|
||||
init_special_inode(inode, inode->i_mode, inode->i_rdev);
|
||||
break;
|
||||
} else {
|
||||
ip->d_ops = ip->i_mount->m_nondir_inode_ops;
|
||||
lockdep_set_class(&ip->i_lock.mr_lock, &xfs_nondir_ilock_class);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1270,3 +1276,35 @@ xfs_setup_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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
@ -303,19 +300,11 @@ xfs_symlink(
|
||||
* If the symlink will fit into the inode, write it inline.
|
||||
*/
|
||||
if (pathlen <= XFS_IFORK_DSIZE(ip)) {
|
||||
xfs_idata_realloc(ip, pathlen, XFS_DATA_FORK);
|
||||
memcpy(ip->i_df.if_u1.if_data, target_path, pathlen);
|
||||
xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, 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;
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
|
||||
|
||||
} else {
|
||||
int offset;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user