aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_extfree_item.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-04-02 23:09:21 -0400
committerBen Myers <bpm@sgi.com>2013-04-05 14:25:35 -0400
commit666d644cd72a9ec58b353209ff191d7430f3b357 (patch)
treeeb6918c1d053b8f55a33f57575906acf7beea8b9 /fs/xfs/xfs_extfree_item.c
parent3d6e036193bfa67a8a1cc1908fe910c7a014d183 (diff)
xfs: don't free EFIs before the EFDs are committed
Filesystems are occasionally being shut down with this error: xfs_trans_ail_delete_bulk: attempting to delete a log item that is not in the AIL. It was diagnosed to be related to the EFI/EFD commit order when the EFI and EFD are in different checkpoints and the EFD is committed before the EFI here: http://oss.sgi.com/archives/xfs/2013-01/msg00082.html The real problem is that a single bit cannot fully describe the states that the EFI/EFD processing can be in. These completion states are: EFI EFI in AIL EFD Result committed/unpinned Yes committed OK committed/pinned No committed Shutdown uncommitted No committed Shutdown Note that the "result" field is what should happen, not what does happen. The current logic is broken and handles the first two cases correctly by luck. That is, the code will free the EFI if the XFS_EFI_COMMITTED bit is *not* set, rather than if it is set. The inverted logic "works" because if both EFI and EFD are committed, then the first __xfs_efi_release() call clears the XFS_EFI_COMMITTED bit, and the second frees the EFI item. Hence as long as xfs_efi_item_committed() has been called, everything appears to be fine. It is the third case where the logic fails - where xfs_efd_item_committed() is called before xfs_efi_item_committed(), and that results in the EFI being freed before it has been committed. That is the bug that triggered the shutdown, and hence keeping track of whether the EFI has been committed or not is insufficient to correctly order the EFI/EFD operations w.r.t. the AIL. What we really want is this: the EFI is always placed into the AIL before the last reference goes away. The only way to guarantee that is that the EFI is not freed until after it has been unpinned *and* the EFD has been committed. That is, restructure the logic so that the only case that can occur is the first case. This can be done easily by replacing the XFS_EFI_COMMITTED with an EFI reference count. The EFI is initialised with it's own count, and that is not released until it is unpinned. However, there is a complication to this method - the high level EFI/EFD code in xfs_bmap_finish() does not hold direct references to the EFI structure, and runs a transaction commit between the EFI and EFD processing. Hence the EFI can be freed even before the EFD is created using such a method. Further, log recovery uses the AIL for tracking EFI/EFDs that need to be recovered, but it uses the AIL *differently* to the EFI transaction commit. Hence log recovery never pins or unpins EFIs, so we can't drop the EFI reference count indirectly to free the EFI. However, this doesn't prevent us from using a reference count here. There is a 1:1 relationship between EFIs and EFDs, so when we initialise the EFI we can take a reference count for the EFD as well. This solves the xfs_bmap_finish() issue - the EFI will never be freed until the EFD is processed. In terms of log recovery, during the committing of the EFD we can look for the XFS_EFI_RECOVERED bit being set and drop the EFI reference as well, thereby ensuring everything works correctly there as well. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Mark Tinguely <tinguely@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_extfree_item.c')
-rw-r--r--fs/xfs/xfs_extfree_item.c27
1 files changed, 13 insertions, 14 deletions
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index feb36d7551ae..c0f375087efc 100644
--- a/fs/xfs/xfs_extfree_item.c
+++ b/fs/xfs/xfs_extfree_item.c
@@ -50,9 +50,8 @@ xfs_efi_item_free(
50 * Freeing the efi requires that we remove it from the AIL if it has already 50 * Freeing the efi requires that we remove it from the AIL if it has already
51 * been placed there. However, the EFI may not yet have been placed in the AIL 51 * been placed there. However, the EFI may not yet have been placed in the AIL
52 * when called by xfs_efi_release() from EFD processing due to the ordering of 52 * when called by xfs_efi_release() from EFD processing due to the ordering of
53 * committed vs unpin operations in bulk insert operations. Hence the 53 * committed vs unpin operations in bulk insert operations. Hence the reference
54 * test_and_clear_bit(XFS_EFI_COMMITTED) to ensure only the last caller frees 54 * count to ensure only the last caller frees the EFI.
55 * the EFI.
56 */ 55 */
57STATIC void 56STATIC void
58__xfs_efi_release( 57__xfs_efi_release(
@@ -60,7 +59,7 @@ __xfs_efi_release(
60{ 59{
61 struct xfs_ail *ailp = efip->efi_item.li_ailp; 60 struct xfs_ail *ailp = efip->efi_item.li_ailp;
62 61
63 if (!test_and_clear_bit(XFS_EFI_COMMITTED, &efip->efi_flags)) { 62 if (atomic_dec_and_test(&efip->efi_refcount)) {
64 spin_lock(&ailp->xa_lock); 63 spin_lock(&ailp->xa_lock);
65 /* xfs_trans_ail_delete() drops the AIL lock. */ 64 /* xfs_trans_ail_delete() drops the AIL lock. */
66 xfs_trans_ail_delete(ailp, &efip->efi_item, 65 xfs_trans_ail_delete(ailp, &efip->efi_item,
@@ -126,8 +125,8 @@ xfs_efi_item_pin(
126 * which the EFI is manipulated during a transaction. If we are being asked to 125 * which the EFI is manipulated during a transaction. If we are being asked to
127 * remove the EFI it's because the transaction has been cancelled and by 126 * remove the EFI it's because the transaction has been cancelled and by
128 * definition that means the EFI cannot be in the AIL so remove it from the 127 * definition that means the EFI cannot be in the AIL so remove it from the
129 * transaction and free it. Otherwise coordinate with xfs_efi_release() (via 128 * transaction and free it. Otherwise coordinate with xfs_efi_release()
130 * XFS_EFI_COMMITTED) to determine who gets to free the EFI. 129 * to determine who gets to free the EFI.
131 */ 130 */
132STATIC void 131STATIC void
133xfs_efi_item_unpin( 132xfs_efi_item_unpin(
@@ -171,19 +170,13 @@ xfs_efi_item_unlock(
171 170
172/* 171/*
173 * The EFI is logged only once and cannot be moved in the log, so simply return 172 * The EFI is logged only once and cannot be moved in the log, so simply return
174 * the lsn at which it's been logged. For bulk transaction committed 173 * the lsn at which it's been logged.
175 * processing, the EFI may be processed but not yet unpinned prior to the EFD
176 * being processed. Set the XFS_EFI_COMMITTED flag so this case can be detected
177 * when processing the EFD.
178 */ 174 */
179STATIC xfs_lsn_t 175STATIC xfs_lsn_t
180xfs_efi_item_committed( 176xfs_efi_item_committed(
181 struct xfs_log_item *lip, 177 struct xfs_log_item *lip,
182 xfs_lsn_t lsn) 178 xfs_lsn_t lsn)
183{ 179{
184 struct xfs_efi_log_item *efip = EFI_ITEM(lip);
185
186 set_bit(XFS_EFI_COMMITTED, &efip->efi_flags);
187 return lsn; 180 return lsn;
188} 181}
189 182
@@ -241,6 +234,7 @@ xfs_efi_init(
241 efip->efi_format.efi_nextents = nextents; 234 efip->efi_format.efi_nextents = nextents;
242 efip->efi_format.efi_id = (__psint_t)(void*)efip; 235 efip->efi_format.efi_id = (__psint_t)(void*)efip;
243 atomic_set(&efip->efi_next_extent, 0); 236 atomic_set(&efip->efi_next_extent, 0);
237 atomic_set(&efip->efi_refcount, 2);
244 238
245 return efip; 239 return efip;
246} 240}
@@ -310,8 +304,13 @@ xfs_efi_release(xfs_efi_log_item_t *efip,
310 uint nextents) 304 uint nextents)
311{ 305{
312 ASSERT(atomic_read(&efip->efi_next_extent) >= nextents); 306 ASSERT(atomic_read(&efip->efi_next_extent) >= nextents);
313 if (atomic_sub_and_test(nextents, &efip->efi_next_extent)) 307 if (atomic_sub_and_test(nextents, &efip->efi_next_extent)) {
314 __xfs_efi_release(efip); 308 __xfs_efi_release(efip);
309
310 /* recovery needs us to drop the EFI reference, too */
311 if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags))
312 __xfs_efi_release(efip);
313 }
315} 314}
316 315
317static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) 316static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)