diff options
author | Christoph Hellwig <hch@lst.de> | 2016-04-05 17:53:29 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2016-04-05 17:53:29 -0400 |
commit | 30ee052e12b97c190b27fe6f20e3ac3047df7b5c (patch) | |
tree | 92b34446e88e72e6ddce730b1f1b4192614a45be | |
parent | bfe8804d908a791b16e3686c101f0d7eca9fb5b9 (diff) |
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>
-rw-r--r-- | fs/xfs/libxfs/xfs_inode_fork.c | 22 | ||||
-rw-r--r-- | fs/xfs/xfs_inode_item.c | 4 | ||||
-rw-r--r-- | fs/xfs/xfs_iops.c | 29 | ||||
-rw-r--r-- | fs/xfs/xfs_symlink.c | 9 |
4 files changed, 49 insertions, 15 deletions
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index 86a97f8a9de3..4fbe2263c1fc 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c | |||
@@ -239,19 +239,33 @@ xfs_init_local_fork( | |||
239 | int size) | 239 | int size) |
240 | { | 240 | { |
241 | struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); | 241 | struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); |
242 | int real_size = 0; | 242 | int mem_size = size, real_size = 0; |
243 | bool zero_terminate; | ||
244 | |||
245 | /* | ||
246 | * If we are using the local fork to store a symlink body we need to | ||
247 | * zero-terminate it so that we can pass it back to the VFS directly. | ||
248 | * Overallocate the in-memory fork by one for that and add a zero | ||
249 | * to terminate it below. | ||
250 | */ | ||
251 | zero_terminate = S_ISLNK(VFS_I(ip)->i_mode); | ||
252 | if (zero_terminate) | ||
253 | mem_size++; | ||
243 | 254 | ||
244 | if (size == 0) | 255 | if (size == 0) |
245 | ifp->if_u1.if_data = NULL; | 256 | ifp->if_u1.if_data = NULL; |
246 | else if (size <= sizeof(ifp->if_u2.if_inline_data)) | 257 | else if (mem_size <= sizeof(ifp->if_u2.if_inline_data)) |
247 | ifp->if_u1.if_data = ifp->if_u2.if_inline_data; | 258 | ifp->if_u1.if_data = ifp->if_u2.if_inline_data; |
248 | else { | 259 | else { |
249 | real_size = roundup(size, 4); | 260 | real_size = roundup(mem_size, 4); |
250 | ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS); | 261 | ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS); |
251 | } | 262 | } |
252 | 263 | ||
253 | if (size) | 264 | if (size) { |
254 | memcpy(ifp->if_u1.if_data, data, size); | 265 | memcpy(ifp->if_u1.if_data, data, size); |
266 | if (zero_terminate) | ||
267 | ifp->if_u1.if_data[size] = '\0'; | ||
268 | } | ||
255 | 269 | ||
256 | ifp->if_bytes = size; | 270 | ifp->if_bytes = size; |
257 | ifp->if_real_bytes = real_size; | 271 | ifp->if_real_bytes = real_size; |
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index c48b5b18d771..37e23c7a5684 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c | |||
@@ -210,7 +210,7 @@ xfs_inode_item_format_data_fork( | |||
210 | */ | 210 | */ |
211 | data_bytes = roundup(ip->i_df.if_bytes, 4); | 211 | data_bytes = roundup(ip->i_df.if_bytes, 4); |
212 | ASSERT(ip->i_df.if_real_bytes == 0 || | 212 | ASSERT(ip->i_df.if_real_bytes == 0 || |
213 | ip->i_df.if_real_bytes == data_bytes); | 213 | ip->i_df.if_real_bytes >= data_bytes); |
214 | ASSERT(ip->i_df.if_u1.if_data != NULL); | 214 | ASSERT(ip->i_df.if_u1.if_data != NULL); |
215 | ASSERT(ip->i_d.di_size > 0); | 215 | ASSERT(ip->i_d.di_size > 0); |
216 | xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_ILOCAL, | 216 | xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_ILOCAL, |
@@ -305,7 +305,7 @@ xfs_inode_item_format_attr_fork( | |||
305 | */ | 305 | */ |
306 | data_bytes = roundup(ip->i_afp->if_bytes, 4); | 306 | data_bytes = roundup(ip->i_afp->if_bytes, 4); |
307 | ASSERT(ip->i_afp->if_real_bytes == 0 || | 307 | ASSERT(ip->i_afp->if_real_bytes == 0 || |
308 | ip->i_afp->if_real_bytes == data_bytes); | 308 | ip->i_afp->if_real_bytes >= data_bytes); |
309 | ASSERT(ip->i_afp->if_u1.if_data != NULL); | 309 | ASSERT(ip->i_afp->if_u1.if_data != NULL); |
310 | xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL, | 310 | xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL, |
311 | ip->i_afp->if_u1.if_data, | 311 | ip->i_afp->if_u1.if_data, |
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index f08d91c51b7f..aee06d9a7b6c 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c | |||
@@ -446,6 +446,16 @@ xfs_vn_get_link( | |||
446 | return ERR_PTR(error); | 446 | return ERR_PTR(error); |
447 | } | 447 | } |
448 | 448 | ||
449 | STATIC const char * | ||
450 | xfs_vn_get_link_inline( | ||
451 | struct dentry *dentry, | ||
452 | struct inode *inode, | ||
453 | struct delayed_call *done) | ||
454 | { | ||
455 | ASSERT(XFS_I(inode)->i_df.if_flags & XFS_IFINLINE); | ||
456 | return XFS_I(inode)->i_df.if_u1.if_data; | ||
457 | } | ||
458 | |||
449 | STATIC int | 459 | STATIC int |
450 | xfs_vn_getattr( | 460 | xfs_vn_getattr( |
451 | struct vfsmount *mnt, | 461 | struct vfsmount *mnt, |
@@ -1171,6 +1181,18 @@ static const struct inode_operations xfs_symlink_inode_operations = { | |||
1171 | .update_time = xfs_vn_update_time, | 1181 | .update_time = xfs_vn_update_time, |
1172 | }; | 1182 | }; |
1173 | 1183 | ||
1184 | static const struct inode_operations xfs_inline_symlink_inode_operations = { | ||
1185 | .readlink = generic_readlink, | ||
1186 | .get_link = xfs_vn_get_link_inline, | ||
1187 | .getattr = xfs_vn_getattr, | ||
1188 | .setattr = xfs_vn_setattr, | ||
1189 | .setxattr = generic_setxattr, | ||
1190 | .getxattr = generic_getxattr, | ||
1191 | .removexattr = generic_removexattr, | ||
1192 | .listxattr = xfs_vn_listxattr, | ||
1193 | .update_time = xfs_vn_update_time, | ||
1194 | }; | ||
1195 | |||
1174 | STATIC void | 1196 | STATIC void |
1175 | xfs_diflags_to_iflags( | 1197 | xfs_diflags_to_iflags( |
1176 | struct inode *inode, | 1198 | struct inode *inode, |
@@ -1282,9 +1304,10 @@ xfs_setup_iops( | |||
1282 | inode->i_fop = &xfs_dir_file_operations; | 1304 | inode->i_fop = &xfs_dir_file_operations; |
1283 | break; | 1305 | break; |
1284 | case S_IFLNK: | 1306 | case S_IFLNK: |
1285 | inode->i_op = &xfs_symlink_inode_operations; | 1307 | if (ip->i_df.if_flags & XFS_IFINLINE) |
1286 | if (!(ip->i_df.if_flags & XFS_IFINLINE)) | 1308 | inode->i_op = &xfs_inline_symlink_inode_operations; |
1287 | inode->i_mapping->a_ops = &xfs_address_space_operations; | 1309 | else |
1310 | inode->i_op = &xfs_symlink_inode_operations; | ||
1288 | break; | 1311 | break; |
1289 | default: | 1312 | default: |
1290 | inode->i_op = &xfs_inode_operations; | 1313 | inode->i_op = &xfs_inode_operations; |
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index b69f4a770fc9..5961c1e880c2 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c | |||
@@ -131,6 +131,8 @@ xfs_readlink( | |||
131 | 131 | ||
132 | trace_xfs_readlink(ip); | 132 | trace_xfs_readlink(ip); |
133 | 133 | ||
134 | ASSERT(!(ip->i_df.if_flags & XFS_IFINLINE)); | ||
135 | |||
134 | if (XFS_FORCED_SHUTDOWN(mp)) | 136 | if (XFS_FORCED_SHUTDOWN(mp)) |
135 | return -EIO; | 137 | return -EIO; |
136 | 138 | ||
@@ -150,12 +152,7 @@ xfs_readlink( | |||
150 | } | 152 | } |
151 | 153 | ||
152 | 154 | ||
153 | if (ip->i_df.if_flags & XFS_IFINLINE) { | 155 | error = xfs_readlink_bmap(ip, link); |
154 | memcpy(link, ip->i_df.if_u1.if_data, pathlen); | ||
155 | link[pathlen] = '\0'; | ||
156 | } else { | ||
157 | error = xfs_readlink_bmap(ip, link); | ||
158 | } | ||
159 | 156 | ||
160 | out: | 157 | out: |
161 | xfs_iunlock(ip, XFS_ILOCK_SHARED); | 158 | xfs_iunlock(ip, XFS_ILOCK_SHARED); |