aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--fs/xfs/xfs_extfree_item.c81
-rw-r--r--fs/xfs/xfs_extfree_item.h10
-rw-r--r--fs/xfs/xfs_log_recover.c9
-rw-r--r--fs/xfs/xfs_trans_extfree.c8
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 */
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)
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 */
125typedef struct xfs_efi_log_item { 125typedef 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