]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/xfs/xfs_trans.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[mv-sheeva.git] / fs / xfs / xfs_trans.c
index 7133fd9ab8689aa6debdfcf81b93160e5ca0e8c6..356d6627f581491dbcf3c6eeeeeb09cbab866283 100644 (file)
@@ -533,7 +533,7 @@ xfs_trans_mod_sb(
                return;
        }
 
-       tp->t_flags |= (XFS_TRANS_SB_DIRTY | XFS_TRANS_DIRTY);
+       tp->t_flags |= flags;
 }
 
 /*
@@ -638,11 +638,23 @@ xfs_trans_apply_sb_deltas(
 }
 
 /*
- * xfs_trans_unreserve_and_mod_sb() is called to release unused
- * reservations and apply superblock counter changes to the in-core
- * superblock.
+ * xfs_trans_unreserve_and_mod_sb() is called to release unused reservations
+ * and apply superblock counter changes to the in-core superblock.  The
+ * t_res_fdblocks_delta and t_res_frextents_delta fields are explicitly NOT
+ * applied to the in-core superblock.  The idea is that that has already been
+ * done.
  *
  * This is done efficiently with a single call to xfs_mod_incore_sb_batch().
+ * However, we have to ensure that we only modify each superblock field only
+ * once because the application of the delta values may not be atomic. That can
+ * lead to ENOSPC races occurring if we have two separate modifcations of the
+ * free space counter to put back the entire reservation and then take away
+ * what we used.
+ *
+ * If we are not logging superblock counters, then the inode allocated/free and
+ * used block counts are not updated in the on disk superblock. In this case,
+ * XFS_TRANS_SB_DIRTY will not be set when the transaction is updated but we
+ * still need to update the incore superblock with the changes.
  */
 STATIC void
 xfs_trans_unreserve_and_mod_sb(
@@ -654,42 +666,43 @@ xfs_trans_unreserve_and_mod_sb(
        /* REFERENCED */
        int             error;
        int             rsvd;
+       int64_t         blkdelta = 0;
+       int64_t         rtxdelta = 0;
 
        msbp = msb;
        rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
 
-       /*
-        * Release any reserved blocks.  Any that were allocated
-        * will be taken back again by fdblocks_delta below.
-        */
-       if (tp->t_blk_res > 0) {
+       /* calculate free blocks delta */
+       if (tp->t_blk_res > 0)
+               blkdelta = tp->t_blk_res;
+
+       if ((tp->t_fdblocks_delta != 0) &&
+           (xfs_sb_version_haslazysbcount(&mp->m_sb) ||
+            (tp->t_flags & XFS_TRANS_SB_DIRTY)))
+               blkdelta += tp->t_fdblocks_delta;
+
+       if (blkdelta != 0) {
                msbp->msb_field = XFS_SBS_FDBLOCKS;
-               msbp->msb_delta = tp->t_blk_res;
+               msbp->msb_delta = blkdelta;
                msbp++;
        }
 
-       /*
-        * Release any reserved real time extents .  Any that were
-        * allocated will be taken back again by frextents_delta below.
-        */
-       if (tp->t_rtx_res > 0) {
+       /* calculate free realtime extents delta */
+       if (tp->t_rtx_res > 0)
+               rtxdelta = tp->t_rtx_res;
+
+       if ((tp->t_frextents_delta != 0) &&
+           (tp->t_flags & XFS_TRANS_SB_DIRTY))
+               rtxdelta += tp->t_frextents_delta;
+
+       if (rtxdelta != 0) {
                msbp->msb_field = XFS_SBS_FREXTENTS;
-               msbp->msb_delta = tp->t_rtx_res;
+               msbp->msb_delta = rtxdelta;
                msbp++;
        }
 
-       /*
-        * Apply any superblock modifications to the in-core version.
-        * The t_res_fdblocks_delta and t_res_frextents_delta fields are
-        * explicitly NOT applied to the in-core superblock.
-        * The idea is that that has already been done.
-        *
-        * If we are not logging superblock counters, then the inode
-        * allocated/free and used block counts are not updated in the
-        * on disk superblock. In this case, XFS_TRANS_SB_DIRTY will
-        * not be set when the transaction is updated but we still need
-        * to update the incore superblock with the changes.
-        */
+       /* apply remaining deltas */
+
        if (xfs_sb_version_haslazysbcount(&mp->m_sb) ||
             (tp->t_flags & XFS_TRANS_SB_DIRTY)) {
                if (tp->t_icount_delta != 0) {
@@ -702,19 +715,9 @@ xfs_trans_unreserve_and_mod_sb(
                        msbp->msb_delta = tp->t_ifree_delta;
                        msbp++;
                }
-               if (tp->t_fdblocks_delta != 0) {
-                       msbp->msb_field = XFS_SBS_FDBLOCKS;
-                       msbp->msb_delta = tp->t_fdblocks_delta;
-                       msbp++;
-               }
        }
 
        if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
-               if (tp->t_frextents_delta != 0) {
-                       msbp->msb_field = XFS_SBS_FREXTENTS;
-                       msbp->msb_delta = tp->t_frextents_delta;
-                       msbp++;
-               }
                if (tp->t_dblocks_delta != 0) {
                        msbp->msb_field = XFS_SBS_DBLOCKS;
                        msbp->msb_delta = tp->t_dblocks_delta;