aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Chinner <david@fromorbit.com>2008-10-30 02:38:39 -0400
committerLachlan McIlroy <lachlan@sgi.com>2008-10-30 02:38:39 -0400
commit27d8d5fe0ef9daeaafbdd32b14b32a2211930062 (patch)
treee170fd577c1f6e58bee1cabb5cad75ced643c3ac
parent82fa9012458d867936d7bf130e6e14bdebc6873c (diff)
[XFS] Use a cursor for AIL traversal.
To replace the current generation number ensuring sanity of the AIL traversal, replace it with an external cursor that is linked to the AIL. Basically, we store the next item in the cursor whenever we want to drop the AIL lock to do something to the current item. When we regain the lock. the current item may already be free, so we can't reference it, but the next item in the traversal is already held in the cursor. When we move or delete an object, we search all the active cursors and if there is an item match we clear the cursor(s) that point to the object. This forces the traversal to restart transparently. We don't invalidate the cursor on insert because the cursor still points to a valid item. If the intem is inserted between the current item and the cursor it does not matter; the traversal is considered to be past the insertion point so it will be picked up in the next traversal. Hence traversal restarts pretty much disappear altogether with this method of traversal, which should substantially reduce the overhead of pushing on a busy AIL. Version 2 o add restart logic o comment cursor interface o minor cleanups SGI-PV: 988143 SGI-Modid: xfs-linux-melb:xfs-kern:32347a Signed-off-by: David Chinner <david@fromorbit.com> Signed-off-by: Lachlan McIlroy <lachlan@sgi.com> Signed-off-by: Christoph Hellwig <hch@infradead.org>
-rw-r--r--fs/xfs/xfs_log.c4
-rw-r--r--fs/xfs/xfs_log_recover.c61
-rw-r--r--fs/xfs/xfs_trans_ail.c207
-rw-r--r--fs/xfs/xfs_trans_priv.h55
4 files changed, 218 insertions, 109 deletions
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 0b02c644355..4184085d44a 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -900,7 +900,7 @@ xfs_log_move_tail(xfs_mount_t *mp,
900int 900int
901xfs_log_need_covered(xfs_mount_t *mp) 901xfs_log_need_covered(xfs_mount_t *mp)
902{ 902{
903 int needed = 0, gen; 903 int needed = 0;
904 xlog_t *log = mp->m_log; 904 xlog_t *log = mp->m_log;
905 905
906 if (!xfs_fs_writable(mp)) 906 if (!xfs_fs_writable(mp))
@@ -909,7 +909,7 @@ xfs_log_need_covered(xfs_mount_t *mp)
909 spin_lock(&log->l_icloglock); 909 spin_lock(&log->l_icloglock);
910 if (((log->l_covered_state == XLOG_STATE_COVER_NEED) || 910 if (((log->l_covered_state == XLOG_STATE_COVER_NEED) ||
911 (log->l_covered_state == XLOG_STATE_COVER_NEED2)) 911 (log->l_covered_state == XLOG_STATE_COVER_NEED2))
912 && !xfs_trans_first_ail(mp, &gen) 912 && !xfs_trans_first_ail(mp, NULL)
913 && xlog_iclogs_empty(log)) { 913 && xlog_iclogs_empty(log)) {
914 if (log->l_covered_state == XLOG_STATE_COVER_NEED) 914 if (log->l_covered_state == XLOG_STATE_COVER_NEED)
915 log->l_covered_state = XLOG_STATE_COVER_DONE; 915 log->l_covered_state = XLOG_STATE_COVER_DONE;
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 199c8ea3647..37ba4899f3e 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -54,10 +54,8 @@ STATIC void xlog_recover_insert_item_backq(xlog_recover_item_t **q,
54 xlog_recover_item_t *item); 54 xlog_recover_item_t *item);
55#if defined(DEBUG) 55#if defined(DEBUG)
56STATIC void xlog_recover_check_summary(xlog_t *); 56STATIC void xlog_recover_check_summary(xlog_t *);
57STATIC void xlog_recover_check_ail(xfs_mount_t *, xfs_log_item_t *, int);
58#else 57#else
59#define xlog_recover_check_summary(log) 58#define xlog_recover_check_summary(log)
60#define xlog_recover_check_ail(mp, lip, gen)
61#endif 59#endif
62 60
63 61
@@ -2710,8 +2708,8 @@ xlog_recover_do_efd_trans(
2710 xfs_efd_log_format_t *efd_formatp; 2708 xfs_efd_log_format_t *efd_formatp;
2711 xfs_efi_log_item_t *efip = NULL; 2709 xfs_efi_log_item_t *efip = NULL;
2712 xfs_log_item_t *lip; 2710 xfs_log_item_t *lip;
2713 int gen;
2714 __uint64_t efi_id; 2711 __uint64_t efi_id;
2712 struct xfs_ail_cursor cur;
2715 2713
2716 if (pass == XLOG_RECOVER_PASS1) { 2714 if (pass == XLOG_RECOVER_PASS1) {
2717 return; 2715 return;
@@ -2730,7 +2728,8 @@ xlog_recover_do_efd_trans(
2730 */ 2728 */
2731 mp = log->l_mp; 2729 mp = log->l_mp;
2732 spin_lock(&mp->m_ail_lock); 2730 spin_lock(&mp->m_ail_lock);
2733 lip = xfs_trans_first_ail(mp, &gen); 2731 xfs_trans_ail_cursor_init(mp->m_ail, &cur);
2732 lip = xfs_trans_first_ail(mp, &cur);
2734 while (lip != NULL) { 2733 while (lip != NULL) {
2735 if (lip->li_type == XFS_LI_EFI) { 2734 if (lip->li_type == XFS_LI_EFI) {
2736 efip = (xfs_efi_log_item_t *)lip; 2735 efip = (xfs_efi_log_item_t *)lip;
@@ -2741,11 +2740,13 @@ xlog_recover_do_efd_trans(
2741 */ 2740 */
2742 xfs_trans_delete_ail(mp, lip); 2741 xfs_trans_delete_ail(mp, lip);
2743 xfs_efi_item_free(efip); 2742 xfs_efi_item_free(efip);
2744 return; 2743 spin_lock(&mp->m_ail_lock);
2744 break;
2745 } 2745 }
2746 } 2746 }
2747 lip = xfs_trans_next_ail(mp, lip, &gen, NULL); 2747 lip = xfs_trans_next_ail(mp, &cur);
2748 } 2748 }
2749 xfs_trans_ail_cursor_done(mp->m_ail, &cur);
2749 spin_unlock(&mp->m_ail_lock); 2750 spin_unlock(&mp->m_ail_lock);
2750} 2751}
2751 2752
@@ -3030,33 +3031,6 @@ abort_error:
3030} 3031}
3031 3032
3032/* 3033/*
3033 * Verify that once we've encountered something other than an EFI
3034 * in the AIL that there are no more EFIs in the AIL.
3035 */
3036#if defined(DEBUG)
3037STATIC void
3038xlog_recover_check_ail(
3039 xfs_mount_t *mp,
3040 xfs_log_item_t *lip,
3041 int gen)
3042{
3043 int orig_gen = gen;
3044
3045 do {
3046 ASSERT(lip->li_type != XFS_LI_EFI);
3047 lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
3048 /*
3049 * The check will be bogus if we restart from the
3050 * beginning of the AIL, so ASSERT that we don't.
3051 * We never should since we're holding the AIL lock
3052 * the entire time.
3053 */
3054 ASSERT(gen == orig_gen);
3055 } while (lip != NULL);
3056}
3057#endif /* DEBUG */
3058
3059/*
3060 * When this is called, all of the EFIs which did not have 3034 * When this is called, all of the EFIs which did not have
3061 * corresponding EFDs should be in the AIL. What we do now 3035 * corresponding EFDs should be in the AIL. What we do now
3062 * is free the extents associated with each one. 3036 * is free the extents associated with each one.
@@ -3080,20 +3054,25 @@ xlog_recover_process_efis(
3080{ 3054{
3081 xfs_log_item_t *lip; 3055 xfs_log_item_t *lip;
3082 xfs_efi_log_item_t *efip; 3056 xfs_efi_log_item_t *efip;
3083 int gen;
3084 xfs_mount_t *mp; 3057 xfs_mount_t *mp;
3085 int error = 0; 3058 int error = 0;
3059 struct xfs_ail_cursor cur;
3086 3060
3087 mp = log->l_mp; 3061 mp = log->l_mp;
3088 spin_lock(&mp->m_ail_lock); 3062 spin_lock(&mp->m_ail_lock);
3089 3063
3090 lip = xfs_trans_first_ail(mp, &gen); 3064 xfs_trans_ail_cursor_init(mp->m_ail, &cur);
3065 lip = xfs_trans_first_ail(mp, &cur);
3091 while (lip != NULL) { 3066 while (lip != NULL) {
3092 /* 3067 /*
3093 * We're done when we see something other than an EFI. 3068 * We're done when we see something other than an EFI.
3069 * There should be no EFIs left in the AIL now.
3094 */ 3070 */
3095 if (lip->li_type != XFS_LI_EFI) { 3071 if (lip->li_type != XFS_LI_EFI) {
3096 xlog_recover_check_ail(mp, lip, gen); 3072#ifdef DEBUG
3073 for (; lip; lip = xfs_trans_next_ail(mp, &cur))
3074 ASSERT(lip->li_type != XFS_LI_EFI);
3075#endif
3097 break; 3076 break;
3098 } 3077 }
3099 3078
@@ -3102,17 +3081,19 @@ xlog_recover_process_efis(
3102 */ 3081 */
3103 efip = (xfs_efi_log_item_t *)lip; 3082 efip = (xfs_efi_log_item_t *)lip;
3104 if (efip->efi_flags & XFS_EFI_RECOVERED) { 3083 if (efip->efi_flags & XFS_EFI_RECOVERED) {
3105 lip = xfs_trans_next_ail(mp, lip, &gen, NULL); 3084 lip = xfs_trans_next_ail(mp, &cur);
3106 continue; 3085 continue;
3107 } 3086 }
3108 3087
3109 spin_unlock(&mp->m_ail_lock); 3088 spin_unlock(&mp->m_ail_lock);
3110 error = xlog_recover_process_efi(mp, efip); 3089 error = xlog_recover_process_efi(mp, efip);
3111 if (error)
3112 return error;
3113 spin_lock(&mp->m_ail_lock); 3090 spin_lock(&mp->m_ail_lock);
3114 lip = xfs_trans_next_ail(mp, lip, &gen, NULL); 3091 if (error)
3092 goto out;
3093 lip = xfs_trans_next_ail(mp, &cur);
3115 } 3094 }
3095out:
3096 xfs_trans_ail_cursor_done(mp->m_ail, &cur);
3116 spin_unlock(&mp->m_ail_lock); 3097 spin_unlock(&mp->m_ail_lock);
3117 return error; 3098 return error;
3118} 3099}
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index db72b52cd42..7b8bfcf1d3d 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -99,26 +99,137 @@ xfs_trans_push_ail(
99} 99}
100 100
101/* 101/*
102 * AIL traversal cursor initialisation.
103 *
104 * The cursor keeps track of where our current traversal is up
105 * to by tracking the next ƣtem in the list for us. However, for
106 * this to be safe, removing an object from the AIL needs to invalidate
107 * any cursor that points to it. hence the traversal cursor needs to
108 * be linked to the struct xfs_ail so that deletion can search all the
109 * active cursors for invalidation.
110 *
111 * We don't link the push cursor because it is embedded in the struct
112 * xfs_ail and hence easily findable.
113 */
114void
115xfs_trans_ail_cursor_init(
116 struct xfs_ail *ailp,
117 struct xfs_ail_cursor *cur)
118{
119 cur->item = NULL;
120 if (cur == &ailp->xa_cursors)
121 return;
122
123 cur->next = ailp->xa_cursors.next;
124 ailp->xa_cursors.next = cur;
125}
126
127/*
128 * Set the cursor to the next item, because when we look
129 * up the cursor the current item may have been freed.
130 */
131STATIC void
132xfs_trans_ail_cursor_set(
133 struct xfs_ail *ailp,
134 struct xfs_ail_cursor *cur,
135 struct xfs_log_item *lip)
136{
137 if (lip)
138 cur->item = xfs_ail_next(ailp, lip);
139}
140
141/*
142 * Get the next item in the traversal and advance the cursor.
143 * If the cursor was invalidated (inidicated by a lip of 1),
144 * restart the traversal.
145 */
146STATIC struct xfs_log_item *
147xfs_trans_ail_cursor_next(
148 struct xfs_ail *ailp,
149 struct xfs_ail_cursor *cur)
150{
151 struct xfs_log_item *lip = cur->item;
152
153 if ((__psint_t)lip & 1)
154 lip = xfs_ail_min(ailp);
155 xfs_trans_ail_cursor_set(ailp, cur, lip);
156 return lip;
157}
158
159/*
160 * Invalidate any cursor that is pointing to this item. This is
161 * called when an item is removed from the AIL. Any cursor pointing
162 * to this object is now invalid and the traversal needs to be
163 * terminated so it doesn't reference a freed object. We set the
164 * cursor item to a value of 1 so we can distinguish between an
165 * invalidation and the end of the list when getting the next item
166 * from the cursor.
167 */
168STATIC void
169xfs_trans_ail_cursor_clear(
170 struct xfs_ail *ailp,
171 struct xfs_log_item *lip)
172{
173 struct xfs_ail_cursor *cur;
174
175 /* need to search all cursors */
176 for (cur = &ailp->xa_cursors; cur; cur = cur->next) {
177 if (cur->item == lip)
178 cur->item = (struct xfs_log_item *)
179 ((__psint_t)cur->item | 1);
180 }
181}
182
183/*
184 * Now that the traversal is complete, we need to remove the cursor
185 * from the list of traversing cursors. Avoid removing the embedded
186 * push cursor, but use the fact it is alway present to make the
187 * list deletion simple.
188 */
189void
190xfs_trans_ail_cursor_done(
191 struct xfs_ail *ailp,
192 struct xfs_ail_cursor *done)
193{
194 struct xfs_ail_cursor *prev = NULL;
195 struct xfs_ail_cursor *cur;
196
197 done->item = NULL;
198 if (done == &ailp->xa_cursors)
199 return;
200 prev = &ailp->xa_cursors;
201 for (cur = prev->next; cur; prev = cur, cur = prev->next) {
202 if (cur == done) {
203 prev->next = cur->next;
204 break;
205 }
206 }
207 ASSERT(cur);
208}
209
210/*
102 * Return the item in the AIL with the current lsn. 211 * Return the item in the AIL with the current lsn.
103 * Return the current tree generation number for use 212 * Return the current tree generation number for use
104 * in calls to xfs_trans_next_ail(). 213 * in calls to xfs_trans_next_ail().
105 */ 214 */
106STATIC xfs_log_item_t * 215STATIC xfs_log_item_t *
107xfs_trans_first_push_ail( 216xfs_trans_first_push_ail(
108 xfs_mount_t *mp, 217 struct xfs_ail *ailp,
109 int *gen, 218 struct xfs_ail_cursor *cur,
110 xfs_lsn_t lsn) 219 xfs_lsn_t lsn)
111{ 220{
112 xfs_log_item_t *lip; 221 xfs_log_item_t *lip;
113 222
114 lip = xfs_ail_min(mp->m_ail); 223 lip = xfs_ail_min(ailp);
115 *gen = (int)mp->m_ail->xa_gen; 224 xfs_trans_ail_cursor_set(ailp, cur, lip);
116 if (lsn == 0) 225 if (lsn == 0)
117 return lip; 226 return lip;
118 227
119 list_for_each_entry(lip, &mp->m_ail->xa_ail, li_ail) { 228 list_for_each_entry(lip, &ailp->xa_ail, li_ail) {
120 if (XFS_LSN_CMP(lip->li_lsn, lsn) >= 0) 229 if (XFS_LSN_CMP(lip->li_lsn, lsn) >= 0) {
230 xfs_trans_ail_cursor_set(ailp, cur, lip);
121 return lip; 231 return lip;
232 }
122 } 233 }
123 234
124 return NULL; 235 return NULL;
@@ -137,22 +248,21 @@ xfsaild_push(
137 xfs_lsn_t target = ailp->xa_target; 248 xfs_lsn_t target = ailp->xa_target;
138 xfs_lsn_t lsn; 249 xfs_lsn_t lsn;
139 xfs_log_item_t *lip; 250 xfs_log_item_t *lip;
140 int gen;
141 int restarts;
142 int flush_log, count, stuck; 251 int flush_log, count, stuck;
143 xfs_mount_t *mp = ailp->xa_mount; 252 xfs_mount_t *mp = ailp->xa_mount;
144 253 struct xfs_ail_cursor *cur = &ailp->xa_cursors;
145#define XFS_TRANS_PUSH_AIL_RESTARTS 10
146 254
147 spin_lock(&mp->m_ail_lock); 255 spin_lock(&mp->m_ail_lock);
148 lip = xfs_trans_first_push_ail(mp, &gen, *last_lsn); 256 xfs_trans_ail_cursor_init(ailp, cur);
257 lip = xfs_trans_first_push_ail(ailp, cur, *last_lsn);
149 if (!lip || XFS_FORCED_SHUTDOWN(mp)) { 258 if (!lip || XFS_FORCED_SHUTDOWN(mp)) {
150 /* 259 /*
151 * AIL is empty or our push has reached the end. 260 * AIL is empty or our push has reached the end.
152 */ 261 */
262 xfs_trans_ail_cursor_done(ailp, cur);
153 spin_unlock(&mp->m_ail_lock); 263 spin_unlock(&mp->m_ail_lock);
154 last_pushed_lsn = 0; 264 last_pushed_lsn = 0;
155 goto out; 265 return tout;
156 } 266 }
157 267
158 XFS_STATS_INC(xs_push_ail); 268 XFS_STATS_INC(xs_push_ail);
@@ -170,7 +280,7 @@ xfsaild_push(
170 */ 280 */
171 tout = 10; 281 tout = 10;
172 lsn = lip->li_lsn; 282 lsn = lip->li_lsn;
173 flush_log = stuck = count = restarts = 0; 283 flush_log = stuck = count = 0;
174 while ((XFS_LSN_CMP(lip->li_lsn, target) < 0)) { 284 while ((XFS_LSN_CMP(lip->li_lsn, target) < 0)) {
175 int lock_result; 285 int lock_result;
176 /* 286 /*
@@ -245,13 +355,12 @@ xfsaild_push(
245 if (stuck > 100) 355 if (stuck > 100)
246 break; 356 break;
247 357
248 lip = xfs_trans_next_ail(mp, lip, &gen, &restarts); 358 lip = xfs_trans_ail_cursor_next(ailp, cur);
249 if (lip == NULL) 359 if (lip == NULL)
250 break; 360 break;
251 if (restarts > XFS_TRANS_PUSH_AIL_RESTARTS)
252 break;
253 lsn = lip->li_lsn; 361 lsn = lip->li_lsn;
254 } 362 }
363 xfs_trans_ail_cursor_done(ailp, cur);
255 spin_unlock(&mp->m_ail_lock); 364 spin_unlock(&mp->m_ail_lock);
256 365
257 if (flush_log) { 366 if (flush_log) {
@@ -275,8 +384,7 @@ xfsaild_push(
275 */ 384 */
276 tout += 20; 385 tout += 20;
277 last_pushed_lsn = 0; 386 last_pushed_lsn = 0;
278 } else if ((restarts > XFS_TRANS_PUSH_AIL_RESTARTS) || 387 } else if ((stuck * 100) / count > 90) {
279 ((stuck * 100) / count > 90)) {
280 /* 388 /*
281 * Either there is a lot of contention on the AIL or we 389 * Either there is a lot of contention on the AIL or we
282 * are stuck due to operations in progress. "Stuck" in this 390 * are stuck due to operations in progress. "Stuck" in this
@@ -288,7 +396,6 @@ xfsaild_push(
288 */ 396 */
289 tout += 10; 397 tout += 10;
290 } 398 }
291out:
292 *last_lsn = last_pushed_lsn; 399 *last_lsn = last_pushed_lsn;
293 return tout; 400 return tout;
294} /* xfsaild_push */ 401} /* xfsaild_push */
@@ -348,9 +455,6 @@ xfs_trans_unlocked_item(
348 * we move in the AIL is the minimum one, update the tail lsn in the 455 * we move in the AIL is the minimum one, update the tail lsn in the
349 * log manager. 456 * log manager.
350 * 457 *
351 * Increment the AIL's generation count to indicate that the tree
352 * has changed.
353 *
354 * This function must be called with the AIL lock held. The lock 458 * This function must be called with the AIL lock held. The lock
355 * is dropped before returning. 459 * is dropped before returning.
356 */ 460 */
@@ -368,14 +472,13 @@ xfs_trans_update_ail(
368 if (lip->li_flags & XFS_LI_IN_AIL) { 472 if (lip->li_flags & XFS_LI_IN_AIL) {
369 dlip = xfs_ail_delete(mp->m_ail, lip); 473 dlip = xfs_ail_delete(mp->m_ail, lip);
370 ASSERT(dlip == lip); 474 ASSERT(dlip == lip);
475 xfs_trans_ail_cursor_clear(mp->m_ail, dlip);
371 } else { 476 } else {
372 lip->li_flags |= XFS_LI_IN_AIL; 477 lip->li_flags |= XFS_LI_IN_AIL;
373 } 478 }
374 479
375 lip->li_lsn = lsn; 480 lip->li_lsn = lsn;
376
377 xfs_ail_insert(mp->m_ail, lip); 481 xfs_ail_insert(mp->m_ail, lip);
378 mp->m_ail->xa_gen++;
379 482
380 if (mlip == dlip) { 483 if (mlip == dlip) {
381 mlip = xfs_ail_min(mp->m_ail); 484 mlip = xfs_ail_min(mp->m_ail);
@@ -415,11 +518,11 @@ xfs_trans_delete_ail(
415 mlip = xfs_ail_min(mp->m_ail); 518 mlip = xfs_ail_min(mp->m_ail);
416 dlip = xfs_ail_delete(mp->m_ail, lip); 519 dlip = xfs_ail_delete(mp->m_ail, lip);
417 ASSERT(dlip == lip); 520 ASSERT(dlip == lip);
521 xfs_trans_ail_cursor_clear(mp->m_ail, dlip);
418 522
419 523
420 lip->li_flags &= ~XFS_LI_IN_AIL; 524 lip->li_flags &= ~XFS_LI_IN_AIL;
421 lip->li_lsn = 0; 525 lip->li_lsn = 0;
422 mp->m_ail->xa_gen++;
423 526
424 if (mlip == dlip) { 527 if (mlip == dlip) {
425 mlip = xfs_ail_min(mp->m_ail); 528 mlip = xfs_ail_min(mp->m_ail);
@@ -455,46 +558,29 @@ xfs_trans_delete_ail(
455 */ 558 */
456xfs_log_item_t * 559xfs_log_item_t *
457xfs_trans_first_ail( 560xfs_trans_first_ail(
458 xfs_mount_t *mp, 561 struct xfs_mount *mp,
459 int *gen) 562 struct xfs_ail_cursor *cur)
460{ 563{
461 xfs_log_item_t *lip; 564 xfs_log_item_t *lip;
565 struct xfs_ail *ailp = mp->m_ail;
462 566
463 lip = xfs_ail_min(mp->m_ail); 567 lip = xfs_ail_min(ailp);
464 *gen = (int)mp->m_ail->xa_gen; 568 xfs_trans_ail_cursor_set(ailp, cur, lip);
465 569
466 return lip; 570 return lip;
467} 571}
468 572
469/* 573/*
470 * If the generation count of the tree has not changed since the 574 * Grab the next item in the AIL from the cursor passed in.
471 * caller last took something from the AIL, then return the elmt
472 * in the tree which follows the one given. If the count has changed,
473 * then return the minimum elmt of the AIL and bump the restarts counter
474 * if one is given.
475 */ 575 */
476xfs_log_item_t * 576xfs_log_item_t *
477xfs_trans_next_ail( 577xfs_trans_next_ail(
478 xfs_mount_t *mp, 578 struct xfs_mount *mp,
479 xfs_log_item_t *lip, 579 struct xfs_ail_cursor *cur)
480 int *gen,
481 int *restarts)
482{ 580{
483 xfs_log_item_t *nlip; 581 struct xfs_ail *ailp = mp->m_ail;
484
485 ASSERT(mp && lip && gen);
486 if (mp->m_ail->xa_gen == *gen) {
487 nlip = xfs_ail_next(mp->m_ail, lip);
488 } else {
489 nlip = xfs_ail_min(mp->m_ail);
490 *gen = (int)mp->m_ail->xa_gen;
491 if (restarts != NULL) {
492 XFS_STATS_INC(xs_push_ail_restarts);
493 (*restarts)++;
494 }
495 }
496 582
497 return (nlip); 583 return xfs_trans_ail_cursor_next(ailp, cur);
498} 584}
499 585
500 586
@@ -517,6 +603,7 @@ xfs_trans_ail_init(
517 xfs_mount_t *mp) 603 xfs_mount_t *mp)
518{ 604{
519 struct xfs_ail *ailp; 605 struct xfs_ail *ailp;
606 int error;
520 607
521 ailp = kmem_zalloc(sizeof(struct xfs_ail), KM_MAYFAIL); 608 ailp = kmem_zalloc(sizeof(struct xfs_ail), KM_MAYFAIL);
522 if (!ailp) 609 if (!ailp)
@@ -524,7 +611,15 @@ xfs_trans_ail_init(
524 611
525 ailp->xa_mount = mp; 612 ailp->xa_mount = mp;
526 INIT_LIST_HEAD(&ailp->xa_ail); 613 INIT_LIST_HEAD(&ailp->xa_ail);
527 return xfsaild_start(ailp); 614 error = xfsaild_start(ailp);
615 if (error)
616 goto out_free_ailp;
617 mp->m_ail = ailp;
618 return 0;
619
620out_free_ailp:
621 kmem_free(ailp);
622 return error;
528} 623}
529 624
530void 625void
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h
index 98317fdc33b..f114d388570 100644
--- a/fs/xfs/xfs_trans_priv.h
+++ b/fs/xfs/xfs_trans_priv.h
@@ -44,20 +44,33 @@ xfs_log_busy_slot_t *xfs_trans_add_busy(xfs_trans_t *tp,
44 xfs_extlen_t idx); 44 xfs_extlen_t idx);
45 45
46/* 46/*
47 * From xfs_trans_ail.c 47 * AIL traversal cursor.
48 *
49 * Rather than using a generation number for detecting changes in the ail, use
50 * a cursor that is protected by the ail lock. The aild cursor exists in the
51 * struct xfs_ail, but other traversals can declare it on the stack and link it
52 * to the ail list.
53 *
54 * When an object is deleted from or moved int the AIL, the cursor list is
55 * searched to see if the object is a designated cursor item. If it is, it is
56 * deleted from the cursor so that the next time the cursor is used traversal
57 * will return to the start.
58 *
59 * This means a traversal colliding with a removal will cause a restart of the
60 * list scan, rather than any insertion or deletion anywhere in the list. The
61 * low bit of the item pointer is set if the cursor has been invalidated so
62 * that we can tell the difference between invalidation and reaching the end
63 * of the list to trigger traversal restarts.
48 */ 64 */
49void xfs_trans_update_ail(struct xfs_mount *mp, 65struct xfs_ail_cursor {
50 struct xfs_log_item *lip, xfs_lsn_t lsn) 66 struct xfs_ail_cursor *next;
51 __releases(mp->m_ail_lock); 67 struct xfs_log_item *item;
52void xfs_trans_delete_ail(struct xfs_mount *mp, 68};
53 struct xfs_log_item *lip)
54 __releases(mp->m_ail_lock);
55struct xfs_log_item *xfs_trans_first_ail(struct xfs_mount *, int *);
56struct xfs_log_item *xfs_trans_next_ail(struct xfs_mount *,
57 struct xfs_log_item *, int *, int *);
58 69
59/* 70/*
60 * AIL push thread support 71 * Private AIL structures.
72 *
73 * Eventually we need to drive the locking in here as well.
61 */ 74 */
62struct xfs_ail { 75struct xfs_ail {
63 struct xfs_mount *xa_mount; 76 struct xfs_mount *xa_mount;
@@ -65,8 +78,28 @@ struct xfs_ail {
65 uint xa_gen; 78 uint xa_gen;
66 struct task_struct *xa_task; 79 struct task_struct *xa_task;
67 xfs_lsn_t xa_target; 80 xfs_lsn_t xa_target;
81 struct xfs_ail_cursor xa_cursors;
68}; 82};
69 83
84/*
85 * From xfs_trans_ail.c
86 */
87void xfs_trans_update_ail(struct xfs_mount *mp,
88 struct xfs_log_item *lip, xfs_lsn_t lsn)
89 __releases(mp->m_ail_lock);
90void xfs_trans_delete_ail(struct xfs_mount *mp,
91 struct xfs_log_item *lip)
92 __releases(mp->m_ail_lock);
93struct xfs_log_item *xfs_trans_first_ail(struct xfs_mount *mp,
94 struct xfs_ail_cursor *cur);
95struct xfs_log_item *xfs_trans_next_ail(struct xfs_mount *mp,
96 struct xfs_ail_cursor *cur);
97
98void xfs_trans_ail_cursor_init(struct xfs_ail *ailp,
99 struct xfs_ail_cursor *cur);
100void xfs_trans_ail_cursor_done(struct xfs_ail *ailp,
101 struct xfs_ail_cursor *cur);
102
70long xfsaild_push(struct xfs_ail *, xfs_lsn_t *); 103long xfsaild_push(struct xfs_ail *, xfs_lsn_t *);
71void xfsaild_wakeup(struct xfs_ail *, xfs_lsn_t); 104void xfsaild_wakeup(struct xfs_ail *, xfs_lsn_t);
72int xfsaild_start(struct xfs_ail *); 105int xfsaild_start(struct xfs_ail *);