aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-12-03 07:50:57 -0500
committerBen Myers <bpm@sgi.com>2013-12-11 15:59:20 -0500
commitb3f03bac8132207a20286d5602eda64500c19724 (patch)
tree851ccba3abdaca296a3da9b9a2fb066d4b4ebe61
parentdf8052e7dae00bde6f21b40b6e3e1099770f3afc (diff)
xfs: xfs_dir2_block_to_sf temp buffer allocation fails
If we are using a large directory block size, and memory becomes fragmented, we can get memory allocation failures trying to kmem_alloc(64k) for a temporary buffer. However, there is not need for a directory buffer sized allocation, as the end result ends up in the inode literal area. This is, at most, slightly less than 2k of space, and hence we don't need an allocation larger than that fora temporary buffer. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Ben Myers <bpm@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
-rw-r--r--fs/xfs/xfs_dir2_sf.c58
1 files changed, 34 insertions, 24 deletions
diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c
index aafc6e46cb58..3725fb1b902b 100644
--- a/fs/xfs/xfs_dir2_sf.c
+++ b/fs/xfs/xfs_dir2_sf.c
@@ -170,6 +170,7 @@ xfs_dir2_block_to_sf(
170 char *ptr; /* current data pointer */ 170 char *ptr; /* current data pointer */
171 xfs_dir2_sf_entry_t *sfep; /* shortform entry */ 171 xfs_dir2_sf_entry_t *sfep; /* shortform entry */
172 xfs_dir2_sf_hdr_t *sfp; /* shortform directory header */ 172 xfs_dir2_sf_hdr_t *sfp; /* shortform directory header */
173 xfs_dir2_sf_hdr_t *dst; /* temporary data buffer */
173 174
174 trace_xfs_dir2_block_to_sf(args); 175 trace_xfs_dir2_block_to_sf(args);
175 176
@@ -177,35 +178,20 @@ xfs_dir2_block_to_sf(
177 mp = dp->i_mount; 178 mp = dp->i_mount;
178 179
179 /* 180 /*
180 * Make a copy of the block data, so we can shrink the inode 181 * allocate a temporary destination buffer the size of the inode
181 * and add local data. 182 * to format the data into. Once we have formatted the data, we
183 * can free the block and copy the formatted data into the inode literal
184 * area.
182 */ 185 */
183 hdr = kmem_alloc(mp->m_dirblksize, KM_SLEEP); 186 dst = kmem_alloc(mp->m_sb.sb_inodesize, KM_SLEEP);
184 memcpy(hdr, bp->b_addr, mp->m_dirblksize); 187 hdr = bp->b_addr;
185 logflags = XFS_ILOG_CORE;
186 if ((error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp))) {
187 ASSERT(error != ENOSPC);
188 goto out;
189 }
190 188
191 /* 189 /*
192 * The buffer is now unconditionally gone, whether
193 * xfs_dir2_shrink_inode worked or not.
194 *
195 * Convert the inode to local format.
196 */
197 dp->i_df.if_flags &= ~XFS_IFEXTENTS;
198 dp->i_df.if_flags |= XFS_IFINLINE;
199 dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
200 ASSERT(dp->i_df.if_bytes == 0);
201 xfs_idata_realloc(dp, size, XFS_DATA_FORK);
202 logflags |= XFS_ILOG_DDATA;
203 /*
204 * Copy the header into the newly allocate local space. 190 * Copy the header into the newly allocate local space.
205 */ 191 */
206 sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; 192 sfp = (xfs_dir2_sf_hdr_t *)dst;
207 memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count)); 193 memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count));
208 dp->i_d.di_size = size; 194
209 /* 195 /*
210 * Set up to loop over the block's entries. 196 * Set up to loop over the block's entries.
211 */ 197 */
@@ -258,10 +244,34 @@ xfs_dir2_block_to_sf(
258 ptr += dp->d_ops->data_entsize(dep->namelen); 244 ptr += dp->d_ops->data_entsize(dep->namelen);
259 } 245 }
260 ASSERT((char *)sfep - (char *)sfp == size); 246 ASSERT((char *)sfep - (char *)sfp == size);
247
248 /* now we are done with the block, we can shrink the inode */
249 logflags = XFS_ILOG_CORE;
250 error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp);
251 if (error) {
252 ASSERT(error != ENOSPC);
253 goto out;
254 }
255
256 /*
257 * The buffer is now unconditionally gone, whether
258 * xfs_dir2_shrink_inode worked or not.
259 *
260 * Convert the inode to local format and copy the data in.
261 */
262 dp->i_df.if_flags &= ~XFS_IFEXTENTS;
263 dp->i_df.if_flags |= XFS_IFINLINE;
264 dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
265 ASSERT(dp->i_df.if_bytes == 0);
266 xfs_idata_realloc(dp, size, XFS_DATA_FORK);
267
268 logflags |= XFS_ILOG_DDATA;
269 memcpy(dp->i_df.if_u1.if_data, dst, size);
270 dp->i_d.di_size = size;
261 xfs_dir2_sf_check(args); 271 xfs_dir2_sf_check(args);
262out: 272out:
263 xfs_trans_log_inode(args->trans, dp, logflags); 273 xfs_trans_log_inode(args->trans, dp, logflags);
264 kmem_free(hdr); 274 kmem_free(dst);
265 return error; 275 return error;
266} 276}
267 277