Skip to content

Commit

Permalink
Merge branch 'xfs-4.7-optimise-inline-symlinks' into for-next
Browse files Browse the repository at this point in the history
  • Loading branch information
dchinner committed May 20, 2016
2 parents d6bd961 + 30ee052 commit 5b91135
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 85 deletions.
1 change: 0 additions & 1 deletion fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 3 additions & 6 deletions fs/xfs/libxfs/xfs_dir2_sf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
62 changes: 44 additions & 18 deletions fs/xfs/libxfs/xfs_inode_fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
}

Expand Down
1 change: 1 addition & 0 deletions fs/xfs/libxfs/xfs_inode_fork.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions fs/xfs/xfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
5 changes: 4 additions & 1 deletion fs/xfs/xfs_inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,16 @@ 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
* the VFS level to prevent the rest of the world seeing the inode
* 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);
Expand All @@ -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);
}

Expand Down
4 changes: 2 additions & 2 deletions fs/xfs/xfs_inode_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 2 additions & 16 deletions fs/xfs/xfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ xfs_readlink_by_handle(
{
struct dentry *dentry;
__u32 olen;
void *link;
int error;

if (!capable(CAP_SYS_ADMIN))
Expand All @@ -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;
}
Expand All @@ -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 = 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;
error = d_inode(dentry)->i_op->readlink(dentry, hreq->ohandle, olen);

out_kfree:
kfree(link);
out_dput:
dput(dentry);
return error;
Expand Down
88 changes: 63 additions & 25 deletions fs/xfs/xfs_iops.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ xfs_generic_create(
}
#endif

xfs_setup_iops(ip);

if (tmpfile)
d_tmpfile(dentry, inode);
else
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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);
}

/*
Expand All @@ -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;
}
}
21 changes: 5 additions & 16 deletions fs/xfs/xfs_symlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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);
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;
xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, pathlen);

ip->i_d.di_size = pathlen;
ip->i_d.di_format = XFS_DINODE_FMT_LOCAL;
xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);

} else {
int offset;

Expand Down

0 comments on commit 5b91135

Please sign in to comment.