aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_dir2.c
diff options
context:
space:
mode:
authorDavid Chinner <david@fromorbit.com>2008-10-30 02:38:12 -0400
committerLachlan McIlroy <lachlan@redback.melbourne.sgi.com>2008-11-10 01:51:14 -0500
commit6f9f51adb6ac0a49fce49e01c47dcfc2810c6e9d (patch)
tree0286cc775521b135e481a4eb26391bc3e4cc8950 /fs/xfs/xfs_dir2.c
parent2cf7f0da3ae225848a2ee10d4e216448a770fd00 (diff)
[XFS] Account for allocated blocks when expanding directories
When we create a directory, we reserve a number of blocks for the maximum possible expansion of of the directory due to various btree splits, freespace allocation, etc. Unfortunately, each allocation is not reflected in the total number of blocks still available to the transaction, so the maximal reservation is used over and over again. This leads to problems where an allocation group has only enough blocks for *some* of the allocations required for the directory modification. After the first N allocations, the remaining blocks in the allocation group drops below the total reservation, and subsequent allocations fail because the allocator will not allow the allocation to proceed if the AG does not have the enough blocks available for the entire allocation total. This results in an ENOSPC occurring after an allocation has already occurred. This results in aborting the directory operation (leaving the directory in an inconsistent state) and cancelling a dirty transaction, which results in a filesystem shutdown. Avoid the problem by reflecting the number of blocks allocated in any directory expansion in the total number of blocks available to the modification in progress. This prevents a directory modification from being aborted part way through with an ENOSPC. SGI-PV: 988144 SGI-Modid: xfs-linux-melb:xfs-kern:32340a Signed-off-by: David Chinner <david@fromorbit.com> Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_dir2.c')
-rw-r--r--fs/xfs/xfs_dir2.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
index 80e0dc51361c..1afb12278b8d 100644
--- a/fs/xfs/xfs_dir2.c
+++ b/fs/xfs/xfs_dir2.c
@@ -525,11 +525,13 @@ xfs_dir2_grow_inode(
525 xfs_mount_t *mp; 525 xfs_mount_t *mp;
526 int nmap; /* number of bmap entries */ 526 int nmap; /* number of bmap entries */
527 xfs_trans_t *tp; 527 xfs_trans_t *tp;
528 xfs_drfsbno_t nblks;
528 529
529 xfs_dir2_trace_args_s("grow_inode", args, space); 530 xfs_dir2_trace_args_s("grow_inode", args, space);
530 dp = args->dp; 531 dp = args->dp;
531 tp = args->trans; 532 tp = args->trans;
532 mp = dp->i_mount; 533 mp = dp->i_mount;
534 nblks = dp->i_d.di_nblocks;
533 /* 535 /*
534 * Set lowest possible block in the space requested. 536 * Set lowest possible block in the space requested.
535 */ 537 */
@@ -622,7 +624,11 @@ xfs_dir2_grow_inode(
622 */ 624 */
623 if (mapp != &map) 625 if (mapp != &map)
624 kmem_free(mapp); 626 kmem_free(mapp);
627
628 /* account for newly allocated blocks in reserved blocks total */
629 args->total -= dp->i_d.di_nblocks - nblks;
625 *dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno); 630 *dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno);
631
626 /* 632 /*
627 * Update file's size if this is the data space and it grew. 633 * Update file's size if this is the data space and it grew.
628 */ 634 */