aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_trans_ail.c
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 /fs/xfs/xfs_trans_ail.c
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>
Diffstat (limited to 'fs/xfs/xfs_trans_ail.c')
-rw-r--r--fs/xfs/xfs_trans_ail.c207
1 files changed, 151 insertions, 56 deletions
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index db72b52cd428..7b8bfcf1d3da 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