diff options
-rw-r--r-- | fs/xfs/xfs_extfree_item.c | 27 | ||||
-rw-r--r-- | fs/xfs/xfs_extfree_item.h | 14 | ||||
-rw-r--r-- | fs/xfs/xfs_log_recover.c | 1 |
3 files changed, 23 insertions, 19 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 | */ |
57 | STATIC void | 56 | STATIC 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 | */ |
132 | STATIC void | 131 | STATIC void |
133 | xfs_efi_item_unpin( | 132 | xfs_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 | */ |
179 | STATIC xfs_lsn_t | 175 | STATIC xfs_lsn_t |
180 | xfs_efi_item_committed( | 176 | xfs_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 | ||
317 | static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) | 316 | static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) |
diff --git a/fs/xfs/xfs_extfree_item.h b/fs/xfs/xfs_extfree_item.h index 375f68e42531..432222418c56 100644 --- a/fs/xfs/xfs_extfree_item.h +++ b/fs/xfs/xfs_extfree_item.h | |||
@@ -114,16 +114,20 @@ typedef struct xfs_efd_log_format_64 { | |||
114 | * Define EFI flag bits. Manipulated by set/clear/test_bit operators. | 114 | * Define EFI flag bits. Manipulated by set/clear/test_bit operators. |
115 | */ | 115 | */ |
116 | #define XFS_EFI_RECOVERED 1 | 116 | #define XFS_EFI_RECOVERED 1 |
117 | #define XFS_EFI_COMMITTED 2 | ||
118 | 117 | ||
119 | /* | 118 | /* |
120 | * This is the "extent free intention" log item. It is used | 119 | * This is the "extent free intention" log item. It is used to log the fact |
121 | * to log the fact that some extents need to be free. It is | 120 | * that some extents need to be free. It is used in conjunction with the |
122 | * used in conjunction with the "extent free done" log item | 121 | * "extent free done" log item described below. |
123 | * described below. | 122 | * |
123 | * The EFI is reference counted so that it is not freed prior to both the EFI | ||
124 | * and EFD being committed and unpinned. This ensures that when the last | ||
125 | * reference goes away the EFI will always be in the AIL as it has been | ||
126 | * unpinned, regardless of whether the EFD is processed before or after the EFI. | ||
124 | */ | 127 | */ |
125 | typedef struct xfs_efi_log_item { | 128 | typedef struct xfs_efi_log_item { |
126 | xfs_log_item_t efi_item; | 129 | xfs_log_item_t efi_item; |
130 | atomic_t efi_refcount; | ||
127 | atomic_t efi_next_extent; | 131 | atomic_t efi_next_extent; |
128 | unsigned long efi_flags; /* misc flags */ | 132 | unsigned long efi_flags; /* misc flags */ |
129 | xfs_efi_log_format_t efi_format; | 133 | xfs_efi_log_format_t efi_format; |
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index d1dba7ce75ae..3ca3380c3afe 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c | |||
@@ -2948,6 +2948,7 @@ xlog_recover_process_efi( | |||
2948 | * This will pull the EFI from the AIL and | 2948 | * This will pull the EFI from the AIL and |
2949 | * free the memory associated with it. | 2949 | * free the memory associated with it. |
2950 | */ | 2950 | */ |
2951 | set_bit(XFS_EFI_RECOVERED, &efip->efi_flags); | ||
2951 | xfs_efi_release(efip, efip->efi_format.efi_nextents); | 2952 | xfs_efi_release(efip, efip->efi_format.efi_nextents); |
2952 | return XFS_ERROR(EIO); | 2953 | return XFS_ERROR(EIO); |
2953 | } | 2954 | } |