aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_extfree_item.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2010-12-19 19:59:49 -0500
committerDave Chinner <david@fromorbit.com>2010-12-19 19:59:49 -0500
commitb199c8a4ba11879df87daad496ceee41fdc6aa82 (patch)
tree8652785ca70788e3cc43272be72f21123adafbe7 /fs/xfs/xfs_extfree_item.c
parent9c5f8414efd5eeed9f498d4170337a3eb126341f (diff)
xfs: Pull EFI/EFD handling out from under the AIL lock
EFI/EFD interactions are protected from races by the AIL lock. They are the only type of log items that require the the AIL lock to serialise internal state, so they need to be separated from the AIL lock before we can do bulk insert operations on the AIL. To acheive this, convert the counter of the number of extents in the EFI to an atomic so it can be safely manipulated by EFD processing without locks. Also, convert the EFI state flag manipulations to use atomic bit operations so no locks are needed to record state changes. Finally, use the state bits to determine when it is safe to free the EFI and clean up the code to do this neatly. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'fs/xfs/xfs_extfree_item.c')
-rw-r--r--fs/xfs/xfs_extfree_item.c81
1 files changed, 44 insertions, 37 deletions
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index 5997efae05d..75f2ef60e57 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 */
58STATIC 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 */
108STATIC void 132STATIC void
109xfs_efi_item_unpin( 133xfs_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 */
158STATIC xfs_lsn_t 180STATIC xfs_lsn_t
159xfs_efi_item_committed( 181xfs_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 */
300void 321void
301xfs_efi_release(xfs_efi_log_item_t *efip, 322xfs_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
323static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) 330static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)