aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDavid Chinner <dgc@sgi.com>2007-06-18 02:49:44 -0400
committerTim Shimmin <tes@chook.melbourne.sgi.com>2007-07-14 01:32:09 -0400
commit45c34141126a89da07197d5b89c04c6847f1171a (patch)
treeb5418b397602fc0ab131ed4a56a90f215628d776 /fs
parentb2826136a1fc3ea451bcbb73a75ca50b3231aa8f (diff)
[XFS] Apply transaction delta counts atomically to incore counters
With the per-cpu superblock counters, batch updates are no longer atomic across the entire batch of changes. This is not an issue if each individual change in the batch is applied atomically. Unfortunately, free block count changes are not applied atomically, and they are applied in a manner guaranteed to cause problems. Essentially, the free block count reservation that the transaction took initially is returned to the in core counters before a second delta takes away what is used. because these two operations are not atomic, we can race with another thread that can use the returned transaction reservation before the transaction takes the space away again and we can then get ENOSPC being reported in a spot where we don't have an ENOSPC condition, nor should we ever see one there. Fix it up by rolling the two deltas into the one so it can be applied safely (i.e. atomically) to the incore counters. SGI-PV: 964465 SGI-Modid: xfs-linux-melb:xfs-kern:28796a Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Christoph Hellwig <hch@infradead.org> Signed-off-by: Tim Shimmin <tes@sgi.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/xfs/xfs_trans.c77
1 files changed, 40 insertions, 37 deletions
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 2caa0783049b..356d6627f581 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -638,11 +638,23 @@ xfs_trans_apply_sb_deltas(
638} 638}
639 639
640/* 640/*
641 * xfs_trans_unreserve_and_mod_sb() is called to release unused 641 * xfs_trans_unreserve_and_mod_sb() is called to release unused reservations
642 * reservations and apply superblock counter changes to the in-core 642 * and apply superblock counter changes to the in-core superblock. The
643 * superblock. 643 * t_res_fdblocks_delta and t_res_frextents_delta fields are explicitly NOT
644 * applied to the in-core superblock. The idea is that that has already been
645 * done.
644 * 646 *
645 * This is done efficiently with a single call to xfs_mod_incore_sb_batch(). 647 * This is done efficiently with a single call to xfs_mod_incore_sb_batch().
648 * However, we have to ensure that we only modify each superblock field only
649 * once because the application of the delta values may not be atomic. That can
650 * lead to ENOSPC races occurring if we have two separate modifcations of the
651 * free space counter to put back the entire reservation and then take away
652 * what we used.
653 *
654 * If we are not logging superblock counters, then the inode allocated/free and
655 * used block counts are not updated in the on disk superblock. In this case,
656 * XFS_TRANS_SB_DIRTY will not be set when the transaction is updated but we
657 * still need to update the incore superblock with the changes.
646 */ 658 */
647STATIC void 659STATIC void
648xfs_trans_unreserve_and_mod_sb( 660xfs_trans_unreserve_and_mod_sb(
@@ -654,42 +666,43 @@ xfs_trans_unreserve_and_mod_sb(
654 /* REFERENCED */ 666 /* REFERENCED */
655 int error; 667 int error;
656 int rsvd; 668 int rsvd;
669 int64_t blkdelta = 0;
670 int64_t rtxdelta = 0;
657 671
658 msbp = msb; 672 msbp = msb;
659 rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0; 673 rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
660 674
661 /* 675 /* calculate free blocks delta */
662 * Release any reserved blocks. Any that were allocated 676 if (tp->t_blk_res > 0)
663 * will be taken back again by fdblocks_delta below. 677 blkdelta = tp->t_blk_res;
664 */ 678
665 if (tp->t_blk_res > 0) { 679 if ((tp->t_fdblocks_delta != 0) &&
680 (xfs_sb_version_haslazysbcount(&mp->m_sb) ||
681 (tp->t_flags & XFS_TRANS_SB_DIRTY)))
682 blkdelta += tp->t_fdblocks_delta;
683
684 if (blkdelta != 0) {
666 msbp->msb_field = XFS_SBS_FDBLOCKS; 685 msbp->msb_field = XFS_SBS_FDBLOCKS;
667 msbp->msb_delta = tp->t_blk_res; 686 msbp->msb_delta = blkdelta;
668 msbp++; 687 msbp++;
669 } 688 }
670 689
671 /* 690 /* calculate free realtime extents delta */
672 * Release any reserved real time extents . Any that were 691 if (tp->t_rtx_res > 0)
673 * allocated will be taken back again by frextents_delta below. 692 rtxdelta = tp->t_rtx_res;
674 */ 693
675 if (tp->t_rtx_res > 0) { 694 if ((tp->t_frextents_delta != 0) &&
695 (tp->t_flags & XFS_TRANS_SB_DIRTY))
696 rtxdelta += tp->t_frextents_delta;
697
698 if (rtxdelta != 0) {
676 msbp->msb_field = XFS_SBS_FREXTENTS; 699 msbp->msb_field = XFS_SBS_FREXTENTS;
677 msbp->msb_delta = tp->t_rtx_res; 700 msbp->msb_delta = rtxdelta;
678 msbp++; 701 msbp++;
679 } 702 }
680 703
681 /* 704 /* apply remaining deltas */
682 * Apply any superblock modifications to the in-core version. 705
683 * The t_res_fdblocks_delta and t_res_frextents_delta fields are
684 * explicitly NOT applied to the in-core superblock.
685 * The idea is that that has already been done.
686 *
687 * If we are not logging superblock counters, then the inode
688 * allocated/free and used block counts are not updated in the
689 * on disk superblock. In this case, XFS_TRANS_SB_DIRTY will
690 * not be set when the transaction is updated but we still need
691 * to update the incore superblock with the changes.
692 */
693 if (xfs_sb_version_haslazysbcount(&mp->m_sb) || 706 if (xfs_sb_version_haslazysbcount(&mp->m_sb) ||
694 (tp->t_flags & XFS_TRANS_SB_DIRTY)) { 707 (tp->t_flags & XFS_TRANS_SB_DIRTY)) {
695 if (tp->t_icount_delta != 0) { 708 if (tp->t_icount_delta != 0) {
@@ -702,19 +715,9 @@ xfs_trans_unreserve_and_mod_sb(
702 msbp->msb_delta = tp->t_ifree_delta; 715 msbp->msb_delta = tp->t_ifree_delta;
703 msbp++; 716 msbp++;
704 } 717 }
705 if (tp->t_fdblocks_delta != 0) {
706 msbp->msb_field = XFS_SBS_FDBLOCKS;
707 msbp->msb_delta = tp->t_fdblocks_delta;
708 msbp++;
709 }
710 } 718 }
711 719
712 if (tp->t_flags & XFS_TRANS_SB_DIRTY) { 720 if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
713 if (tp->t_frextents_delta != 0) {
714 msbp->msb_field = XFS_SBS_FREXTENTS;
715 msbp->msb_delta = tp->t_frextents_delta;
716 msbp++;
717 }
718 if (tp->t_dblocks_delta != 0) { 721 if (tp->t_dblocks_delta != 0) {
719 msbp->msb_field = XFS_SBS_DBLOCKS; 722 msbp->msb_field = XFS_SBS_DBLOCKS;
720 msbp->msb_delta = tp->t_dblocks_delta; 723 msbp->msb_delta = tp->t_dblocks_delta;