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 | } |
