diff options
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_extfree_item.c | 81 | ||||
-rw-r--r-- | fs/xfs/xfs_extfree_item.h | 10 | ||||
-rw-r--r-- | fs/xfs/xfs_log_recover.c | 9 | ||||
-rw-r--r-- | fs/xfs/xfs_trans_extfree.c | 8 |
4 files changed, 59 insertions, 49 deletions
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 5997efae05dc..75f2ef60e579 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c | |||
@@ -48,6 +48,28 @@ xfs_efi_item_free( | |||
48 | } | 48 | } |
49 | 49 | ||
50 | /* | 50 | /* |
51 | * Freeing the efi requires that we remove it from the AIL if it has already | ||
52 | * been placed there. However, the EFI may not yet have been placed in the AIL | ||
53 | * when called by xfs_efi_release() from EFD processing due to the ordering of | ||
54 | * committed vs unpin operations in bulk insert operations. Hence the | ||
55 | * test_and_clear_bit(XFS_EFI_COMMITTED) to ensure only the last caller frees | ||
56 | * the EFI. | ||
57 | */ | ||
58 | STATIC void | ||
59 | __xfs_efi_release( | ||
60 | struct xfs_efi_log_item *efip) | ||
61 | { | ||
62 | struct xfs_ail *ailp = efip->efi_item.li_ailp; | ||
63 | |||
64 | if (!test_and_clear_bit(XFS_EFI_COMMITTED, &efip->efi_flags)) { | ||
65 | spin_lock(&ailp->xa_lock); | ||
66 | /* xfs_trans_ail_delete() drops the AIL lock. */ | ||
67 | xfs_trans_ail_delete(ailp, &efip->efi_item); | ||
68 | xfs_efi_item_free(efip); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /* | ||
51 | * This returns the number of iovecs needed to log the given efi item. | 73 | * This returns the number of iovecs needed to log the given efi item. |
52 | * We only need 1 iovec for an efi item. It just logs the efi_log_format | 74 | * We only need 1 iovec for an efi item. It just logs the efi_log_format |
53 | * structure. | 75 | * structure. |
@@ -74,7 +96,8 @@ xfs_efi_item_format( | |||
74 | struct xfs_efi_log_item *efip = EFI_ITEM(lip); | 96 | struct xfs_efi_log_item *efip = EFI_ITEM(lip); |
75 | uint size; | 97 | uint size; |
76 | 98 | ||
77 | ASSERT(efip->efi_next_extent == efip->efi_format.efi_nextents); | 99 | ASSERT(atomic_read(&efip->efi_next_extent) == |
100 | efip->efi_format.efi_nextents); | ||
78 | 101 | ||
79 | efip->efi_format.efi_type = XFS_LI_EFI; | 102 | efip->efi_format.efi_type = XFS_LI_EFI; |
80 | 103 | ||
@@ -103,7 +126,8 @@ xfs_efi_item_pin( | |||
103 | * which the EFI is manipulated during a transaction. If we are being asked to | 126 | * which the EFI is manipulated during a transaction. If we are being asked to |
104 | * remove the EFI it's because the transaction has been cancelled and by | 127 | * remove the EFI it's because the transaction has been cancelled and by |
105 | * definition that means the EFI cannot be in the AIL so remove it from the | 128 | * definition that means the EFI cannot be in the AIL so remove it from the |
106 | * transaction and free it. | 129 | * transaction and free it. Otherwise coordinate with xfs_efi_release() (via |
130 | * XFS_EFI_COMMITTED) to determine who gets to free the EFI. | ||
107 | */ | 131 | */ |
108 | STATIC void | 132 | STATIC void |
109 | xfs_efi_item_unpin( | 133 | xfs_efi_item_unpin( |
@@ -111,17 +135,14 @@ xfs_efi_item_unpin( | |||
111 | int remove) | 135 | int remove) |
112 | { | 136 | { |
113 | struct xfs_efi_log_item *efip = EFI_ITEM(lip); | 137 | struct xfs_efi_log_item *efip = EFI_ITEM(lip); |
114 | struct xfs_ail *ailp = lip->li_ailp; | ||
115 | 138 | ||
116 | spin_lock(&ailp->xa_lock); | ||
117 | if (remove) { | 139 | if (remove) { |
118 | ASSERT(!(lip->li_flags & XFS_LI_IN_AIL)); | 140 | ASSERT(!(lip->li_flags & XFS_LI_IN_AIL)); |
119 | xfs_trans_del_item(lip); | 141 | xfs_trans_del_item(lip); |
120 | xfs_efi_item_free(efip); | 142 | xfs_efi_item_free(efip); |
121 | } else { | 143 | return; |
122 | efip->efi_flags |= XFS_EFI_COMMITTED; | ||
123 | } | 144 | } |
124 | spin_unlock(&ailp->xa_lock); | 145 | __xfs_efi_release(efip); |
125 | } | 146 | } |
126 | 147 | ||
127 | /* | 148 | /* |
@@ -150,16 +171,20 @@ xfs_efi_item_unlock( | |||
150 | } | 171 | } |
151 | 172 | ||
152 | /* | 173 | /* |
153 | * The EFI is logged only once and cannot be moved in the log, so | 174 | * The EFI is logged only once and cannot be moved in the log, so simply return |
154 | * simply return the lsn at which it's been logged. The canceled | 175 | * the lsn at which it's been logged. For bulk transaction committed |
155 | * flag is not paid any attention here. Checking for that is delayed | 176 | * processing, the EFI may be processed but not yet unpinned prior to the EFD |
156 | * until the EFI is unpinned. | 177 | * being processed. Set the XFS_EFI_COMMITTED flag so this case can be detected |
178 | * when processing the EFD. | ||
157 | */ | 179 | */ |
158 | STATIC xfs_lsn_t | 180 | STATIC xfs_lsn_t |
159 | xfs_efi_item_committed( | 181 | xfs_efi_item_committed( |
160 | struct xfs_log_item *lip, | 182 | struct xfs_log_item *lip, |
161 | xfs_lsn_t lsn) | 183 | xfs_lsn_t lsn) |
162 | { | 184 | { |
185 | struct xfs_efi_log_item *efip = EFI_ITEM(lip); | ||
186 | |||
187 | set_bit(XFS_EFI_COMMITTED, &efip->efi_flags); | ||
163 | return lsn; | 188 | return lsn; |
164 | } | 189 | } |
165 | 190 | ||
@@ -228,6 +253,7 @@ xfs_efi_init( | |||
228 | xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops); | 253 | xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops); |
229 | efip->efi_format.efi_nextents = nextents; | 254 | efip->efi_format.efi_nextents = nextents; |
230 | efip->efi_format.efi_id = (__psint_t)(void*)efip; | 255 | efip->efi_format.efi_id = (__psint_t)(void*)efip; |
256 | atomic_set(&efip->efi_next_extent, 0); | ||
231 | 257 | ||
232 | return efip; | 258 | return efip; |
233 | } | 259 | } |
@@ -287,37 +313,18 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) | |||
287 | } | 313 | } |
288 | 314 | ||
289 | /* | 315 | /* |
290 | * This is called by the efd item code below to release references to | 316 | * This is called by the efd item code below to release references to the given |
291 | * the given efi item. Each efd calls this with the number of | 317 | * efi item. Each efd calls this with the number of extents that it has |
292 | * extents that it has logged, and when the sum of these reaches | 318 | * logged, and when the sum of these reaches the total number of extents logged |
293 | * the total number of extents logged by this efi item we can free | 319 | * by this efi item we can free the efi item. |
294 | * the efi item. | ||
295 | * | ||
296 | * Freeing the efi item requires that we remove it from the AIL. | ||
297 | * We'll use the AIL lock to protect our counters as well as | ||
298 | * the removal from the AIL. | ||
299 | */ | 320 | */ |
300 | void | 321 | void |
301 | xfs_efi_release(xfs_efi_log_item_t *efip, | 322 | xfs_efi_release(xfs_efi_log_item_t *efip, |
302 | uint nextents) | 323 | uint nextents) |
303 | { | 324 | { |
304 | struct xfs_ail *ailp = efip->efi_item.li_ailp; | 325 | ASSERT(atomic_read(&efip->efi_next_extent) >= nextents); |
305 | int extents_left; | 326 | if (atomic_sub_and_test(nextents, &efip->efi_next_extent)) |
306 | 327 | __xfs_efi_release(efip); | |
307 | ASSERT(efip->efi_next_extent > 0); | ||
308 | ASSERT(efip->efi_flags & XFS_EFI_COMMITTED); | ||
309 | |||
310 | spin_lock(&ailp->xa_lock); | ||
311 | ASSERT(efip->efi_next_extent >= nextents); | ||
312 | efip->efi_next_extent -= nextents; | ||
313 | extents_left = efip->efi_next_extent; | ||
314 | if (extents_left == 0) { | ||
315 | /* xfs_trans_ail_delete() drops the AIL lock. */ | ||
316 | xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip); | ||
317 | xfs_efi_item_free(efip); | ||
318 | } else { | ||
319 | spin_unlock(&ailp->xa_lock); | ||
320 | } | ||
321 | } | 328 | } |
322 | 329 | ||
323 | static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) | 330 | 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 f7834ec8efad..375f68e42531 100644 --- a/fs/xfs/xfs_extfree_item.h +++ b/fs/xfs/xfs_extfree_item.h | |||
@@ -111,10 +111,10 @@ typedef struct xfs_efd_log_format_64 { | |||
111 | #define XFS_EFI_MAX_FAST_EXTENTS 16 | 111 | #define XFS_EFI_MAX_FAST_EXTENTS 16 |
112 | 112 | ||
113 | /* | 113 | /* |
114 | * Define EFI flags. | 114 | * Define EFI flag bits. Manipulated by set/clear/test_bit operators. |
115 | */ | 115 | */ |
116 | #define XFS_EFI_RECOVERED 0x1 | 116 | #define XFS_EFI_RECOVERED 1 |
117 | #define XFS_EFI_COMMITTED 0x2 | 117 | #define XFS_EFI_COMMITTED 2 |
118 | 118 | ||
119 | /* | 119 | /* |
120 | * This is the "extent free intention" log item. It is used | 120 | * This is the "extent free intention" log item. It is used |
@@ -124,8 +124,8 @@ typedef struct xfs_efd_log_format_64 { | |||
124 | */ | 124 | */ |
125 | typedef struct xfs_efi_log_item { | 125 | typedef struct xfs_efi_log_item { |
126 | xfs_log_item_t efi_item; | 126 | xfs_log_item_t efi_item; |
127 | uint efi_flags; /* misc flags */ | 127 | atomic_t efi_next_extent; |
128 | uint efi_next_extent; | 128 | unsigned long efi_flags; /* misc flags */ |
129 | xfs_efi_log_format_t efi_format; | 129 | xfs_efi_log_format_t efi_format; |
130 | } xfs_efi_log_item_t; | 130 | } xfs_efi_log_item_t; |
131 | 131 | ||
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 4ab4f6ff48aa..d7219e29d9ab 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c | |||
@@ -2567,8 +2567,7 @@ xlog_recover_efi_pass2( | |||
2567 | xfs_efi_item_free(efip); | 2567 | xfs_efi_item_free(efip); |
2568 | return error; | 2568 | return error; |
2569 | } | 2569 | } |
2570 | efip->efi_next_extent = efi_formatp->efi_nextents; | 2570 | atomic_set(&efip->efi_next_extent, efi_formatp->efi_nextents); |
2571 | efip->efi_flags |= XFS_EFI_COMMITTED; | ||
2572 | 2571 | ||
2573 | spin_lock(&log->l_ailp->xa_lock); | 2572 | spin_lock(&log->l_ailp->xa_lock); |
2574 | /* | 2573 | /* |
@@ -2878,7 +2877,7 @@ xlog_recover_process_efi( | |||
2878 | xfs_extent_t *extp; | 2877 | xfs_extent_t *extp; |
2879 | xfs_fsblock_t startblock_fsb; | 2878 | xfs_fsblock_t startblock_fsb; |
2880 | 2879 | ||
2881 | ASSERT(!(efip->efi_flags & XFS_EFI_RECOVERED)); | 2880 | ASSERT(!test_bit(XFS_EFI_RECOVERED, &efip->efi_flags)); |
2882 | 2881 | ||
2883 | /* | 2882 | /* |
2884 | * First check the validity of the extents described by the | 2883 | * First check the validity of the extents described by the |
@@ -2917,7 +2916,7 @@ xlog_recover_process_efi( | |||
2917 | extp->ext_len); | 2916 | extp->ext_len); |
2918 | } | 2917 | } |
2919 | 2918 | ||
2920 | efip->efi_flags |= XFS_EFI_RECOVERED; | 2919 | set_bit(XFS_EFI_RECOVERED, &efip->efi_flags); |
2921 | error = xfs_trans_commit(tp, 0); | 2920 | error = xfs_trans_commit(tp, 0); |
2922 | return error; | 2921 | return error; |
2923 | 2922 | ||
@@ -2974,7 +2973,7 @@ xlog_recover_process_efis( | |||
2974 | * Skip EFIs that we've already processed. | 2973 | * Skip EFIs that we've already processed. |
2975 | */ | 2974 | */ |
2976 | efip = (xfs_efi_log_item_t *)lip; | 2975 | efip = (xfs_efi_log_item_t *)lip; |
2977 | if (efip->efi_flags & XFS_EFI_RECOVERED) { | 2976 | if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags)) { |
2978 | lip = xfs_trans_ail_cursor_next(ailp, &cur); | 2977 | lip = xfs_trans_ail_cursor_next(ailp, &cur); |
2979 | continue; | 2978 | continue; |
2980 | } | 2979 | } |
diff --git a/fs/xfs/xfs_trans_extfree.c b/fs/xfs/xfs_trans_extfree.c index f783d5e9fa70..f7590f5badea 100644 --- a/fs/xfs/xfs_trans_extfree.c +++ b/fs/xfs/xfs_trans_extfree.c | |||
@@ -69,12 +69,16 @@ xfs_trans_log_efi_extent(xfs_trans_t *tp, | |||
69 | tp->t_flags |= XFS_TRANS_DIRTY; | 69 | tp->t_flags |= XFS_TRANS_DIRTY; |
70 | efip->efi_item.li_desc->lid_flags |= XFS_LID_DIRTY; | 70 | efip->efi_item.li_desc->lid_flags |= XFS_LID_DIRTY; |
71 | 71 | ||
72 | next_extent = efip->efi_next_extent; | 72 | /* |
73 | * atomic_inc_return gives us the value after the increment; | ||
74 | * we want to use it as an array index so we need to subtract 1 from | ||
75 | * it. | ||
76 | */ | ||
77 | next_extent = atomic_inc_return(&efip->efi_next_extent) - 1; | ||
73 | ASSERT(next_extent < efip->efi_format.efi_nextents); | 78 | ASSERT(next_extent < efip->efi_format.efi_nextents); |
74 | extp = &(efip->efi_format.efi_extents[next_extent]); | 79 | extp = &(efip->efi_format.efi_extents[next_extent]); |
75 | extp->ext_start = start_block; | 80 | extp->ext_start = start_block; |
76 | extp->ext_len = ext_len; | 81 | extp->ext_len = ext_len; |
77 | efip->efi_next_extent++; | ||
78 | } | 82 | } |
79 | 83 | ||
80 | 84 | ||