diff options
Diffstat (limited to 'fs/xfs/xfs_trans_ail.c')
-rw-r--r-- | fs/xfs/xfs_trans_ail.c | 109 |
1 files changed, 88 insertions, 21 deletions
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 5fc2380092c8..9a69dc06ea86 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c | |||
@@ -272,9 +272,9 @@ xfs_trans_ail_cursor_clear( | |||
272 | } | 272 | } |
273 | 273 | ||
274 | /* | 274 | /* |
275 | * Return the item in the AIL with the current lsn. | 275 | * Initialise the cursor to the first item in the AIL with the given @lsn. |
276 | * Return the current tree generation number for use | 276 | * This searches the list from lowest LSN to highest. Pass a @lsn of zero |
277 | * in calls to xfs_trans_next_ail(). | 277 | * to initialise the cursor to the first item in the AIL. |
278 | */ | 278 | */ |
279 | xfs_log_item_t * | 279 | xfs_log_item_t * |
280 | xfs_trans_ail_cursor_first( | 280 | xfs_trans_ail_cursor_first( |
@@ -300,31 +300,97 @@ out: | |||
300 | } | 300 | } |
301 | 301 | ||
302 | /* | 302 | /* |
303 | * splice the log item list into the AIL at the given LSN. | 303 | * Initialise the cursor to the last item in the AIL with the given @lsn. |
304 | * This searches the list from highest LSN to lowest. If there is no item with | ||
305 | * the value of @lsn, then it sets the cursor to the last item with an LSN lower | ||
306 | * than @lsn. | ||
307 | */ | ||
308 | static struct xfs_log_item * | ||
309 | __xfs_trans_ail_cursor_last( | ||
310 | struct xfs_ail *ailp, | ||
311 | xfs_lsn_t lsn) | ||
312 | { | ||
313 | xfs_log_item_t *lip; | ||
314 | |||
315 | list_for_each_entry_reverse(lip, &ailp->xa_ail, li_ail) { | ||
316 | if (XFS_LSN_CMP(lip->li_lsn, lsn) <= 0) | ||
317 | return lip; | ||
318 | } | ||
319 | return NULL; | ||
320 | } | ||
321 | |||
322 | /* | ||
323 | * Initialise the cursor to the last item in the AIL with the given @lsn. | ||
324 | * This searches the list from highest LSN to lowest. | ||
325 | */ | ||
326 | struct xfs_log_item * | ||
327 | xfs_trans_ail_cursor_last( | ||
328 | struct xfs_ail *ailp, | ||
329 | struct xfs_ail_cursor *cur, | ||
330 | xfs_lsn_t lsn) | ||
331 | { | ||
332 | xfs_trans_ail_cursor_init(ailp, cur); | ||
333 | cur->item = __xfs_trans_ail_cursor_last(ailp, lsn); | ||
334 | return cur->item; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * splice the log item list into the AIL at the given LSN. We splice to the | ||
339 | * tail of the given LSN to maintain insert order for push traversals. The | ||
340 | * cursor is optional, allowing repeated updates to the same LSN to avoid | ||
341 | * repeated traversals. | ||
304 | */ | 342 | */ |
305 | static void | 343 | static void |
306 | xfs_ail_splice( | 344 | xfs_ail_splice( |
307 | struct xfs_ail *ailp, | 345 | struct xfs_ail *ailp, |
308 | struct list_head *list, | 346 | struct xfs_ail_cursor *cur, |
309 | xfs_lsn_t lsn) | 347 | struct list_head *list, |
348 | xfs_lsn_t lsn) | ||
310 | { | 349 | { |
311 | xfs_log_item_t *next_lip; | 350 | struct xfs_log_item *lip = cur ? cur->item : NULL; |
351 | struct xfs_log_item *next_lip; | ||
312 | 352 | ||
313 | /* If the list is empty, just insert the item. */ | 353 | /* |
314 | if (list_empty(&ailp->xa_ail)) { | 354 | * Get a new cursor if we don't have a placeholder or the existing one |
315 | list_splice(list, &ailp->xa_ail); | 355 | * has been invalidated. |
316 | return; | 356 | */ |
357 | if (!lip || (__psint_t)lip & 1) { | ||
358 | lip = __xfs_trans_ail_cursor_last(ailp, lsn); | ||
359 | |||
360 | if (!lip) { | ||
361 | /* The list is empty, so just splice and return. */ | ||
362 | if (cur) | ||
363 | cur->item = NULL; | ||
364 | list_splice(list, &ailp->xa_ail); | ||
365 | return; | ||
366 | } | ||
317 | } | 367 | } |
318 | 368 | ||
319 | list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) { | 369 | /* |
320 | if (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0) | 370 | * Our cursor points to the item we want to insert _after_, so we have |
321 | break; | 371 | * to update the cursor to point to the end of the list we are splicing |
372 | * in so that it points to the correct location for the next splice. | ||
373 | * i.e. before the splice | ||
374 | * | ||
375 | * lsn -> lsn -> lsn + x -> lsn + x ... | ||
376 | * ^ | ||
377 | * | cursor points here | ||
378 | * | ||
379 | * After the splice we have: | ||
380 | * | ||
381 | * lsn -> lsn -> lsn -> lsn -> .... -> lsn -> lsn + x -> lsn + x ... | ||
382 | * ^ ^ | ||
383 | * | cursor points here | needs to move here | ||
384 | * | ||
385 | * So we set the cursor to the last item in the list to be spliced | ||
386 | * before we execute the splice, resulting in the cursor pointing to | ||
387 | * the correct item after the splice occurs. | ||
388 | */ | ||
389 | if (cur) { | ||
390 | next_lip = list_entry(list->prev, struct xfs_log_item, li_ail); | ||
391 | cur->item = next_lip; | ||
322 | } | 392 | } |
323 | 393 | list_splice(list, &lip->li_ail); | |
324 | ASSERT(&next_lip->li_ail == &ailp->xa_ail || | ||
325 | XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0); | ||
326 | |||
327 | list_splice_init(list, &next_lip->li_ail); | ||
328 | } | 394 | } |
329 | 395 | ||
330 | /* | 396 | /* |
@@ -645,6 +711,7 @@ xfs_trans_unlocked_item( | |||
645 | void | 711 | void |
646 | xfs_trans_ail_update_bulk( | 712 | xfs_trans_ail_update_bulk( |
647 | struct xfs_ail *ailp, | 713 | struct xfs_ail *ailp, |
714 | struct xfs_ail_cursor *cur, | ||
648 | struct xfs_log_item **log_items, | 715 | struct xfs_log_item **log_items, |
649 | int nr_items, | 716 | int nr_items, |
650 | xfs_lsn_t lsn) __releases(ailp->xa_lock) | 717 | xfs_lsn_t lsn) __releases(ailp->xa_lock) |
@@ -674,7 +741,7 @@ xfs_trans_ail_update_bulk( | |||
674 | list_add(&lip->li_ail, &tmp); | 741 | list_add(&lip->li_ail, &tmp); |
675 | } | 742 | } |
676 | 743 | ||
677 | xfs_ail_splice(ailp, &tmp, lsn); | 744 | xfs_ail_splice(ailp, cur, &tmp, lsn); |
678 | 745 | ||
679 | if (!mlip_changed) { | 746 | if (!mlip_changed) { |
680 | spin_unlock(&ailp->xa_lock); | 747 | spin_unlock(&ailp->xa_lock); |