]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
xfs: optimize inline symlinks
authorChristoph Hellwig <hch@lst.de>
Tue, 5 Apr 2016 21:53:29 +0000 (07:53 +1000)
committerDave Chinner <david@fromorbit.com>
Tue, 5 Apr 2016 21:53:29 +0000 (07:53 +1000)
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>
fs/xfs/libxfs/xfs_inode_fork.c
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_iops.c
fs/xfs/xfs_symlink.c

index 86a97f8a9de38ad7b13392b6cc086e7700ef203d..4fbe2263c1fc1bb0a3f0e4fc5a0144d99b5c4c68 100644 (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;
index c48b5b18d771fab685e23c03613a1c6e762efcb4..37e23c7a56843257414bcad5e8d7f20a80488679 100644 (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,
index f08d91c51b7f928ad36f191b11091b4e119eb9c6..aee06d9a7b6cb11a6399f6fc7775169bbc2ae5e1 100644 (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;
index b69f4a770fc92d9cbeb6d172c7492a5ea354a0c4..5961c1e880c265c64b53badd7ec3b2a8991e7720 100644 (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);