diff options
author | Dave Chinner <dchinner@redhat.com> | 2010-12-19 19:59:49 -0500 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2010-12-19 19:59:49 -0500 |
commit | b199c8a4ba11879df87daad496ceee41fdc6aa82 (patch) | |
tree | 8652785ca70788e3cc43272be72f21123adafbe7 | |
parent | 9c5f8414efd5eeed9f498d4170337a3eb126341f (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.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 | ||