diff options
Diffstat (limited to 'fs/xfs/xfs_trans_ail.c')
-rw-r--r-- | fs/xfs/xfs_trans_ail.c | 421 |
1 files changed, 231 insertions, 190 deletions
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 12aff9584e29..acdb92f14d51 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c | |||
@@ -28,74 +28,138 @@ | |||
28 | #include "xfs_trans_priv.h" | 28 | #include "xfs_trans_priv.h" |
29 | #include "xfs_error.h" | 29 | #include "xfs_error.h" |
30 | 30 | ||
31 | STATIC void xfs_ail_splice(struct xfs_ail *, struct list_head *, xfs_lsn_t); | 31 | struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ |
32 | STATIC void xfs_ail_delete(struct xfs_ail *, xfs_log_item_t *); | ||
33 | STATIC xfs_log_item_t * xfs_ail_min(struct xfs_ail *); | ||
34 | STATIC xfs_log_item_t * xfs_ail_next(struct xfs_ail *, xfs_log_item_t *); | ||
35 | 32 | ||
36 | #ifdef DEBUG | 33 | #ifdef DEBUG |
37 | STATIC void xfs_ail_check(struct xfs_ail *, xfs_log_item_t *); | 34 | /* |
38 | #else | 35 | * Check that the list is sorted as it should be. |
36 | */ | ||
37 | STATIC void | ||
38 | xfs_ail_check( | ||
39 | struct xfs_ail *ailp, | ||
40 | xfs_log_item_t *lip) | ||
41 | { | ||
42 | xfs_log_item_t *prev_lip; | ||
43 | |||
44 | if (list_empty(&ailp->xa_ail)) | ||
45 | return; | ||
46 | |||
47 | /* | ||
48 | * Check the next and previous entries are valid. | ||
49 | */ | ||
50 | ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0); | ||
51 | prev_lip = list_entry(lip->li_ail.prev, xfs_log_item_t, li_ail); | ||
52 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
53 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0); | ||
54 | |||
55 | prev_lip = list_entry(lip->li_ail.next, xfs_log_item_t, li_ail); | ||
56 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
57 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) >= 0); | ||
58 | |||
59 | |||
60 | #ifdef XFS_TRANS_DEBUG | ||
61 | /* | ||
62 | * Walk the list checking lsn ordering, and that every entry has the | ||
63 | * XFS_LI_IN_AIL flag set. This is really expensive, so only do it | ||
64 | * when specifically debugging the transaction subsystem. | ||
65 | */ | ||
66 | prev_lip = list_entry(&ailp->xa_ail, xfs_log_item_t, li_ail); | ||
67 | list_for_each_entry(lip, &ailp->xa_ail, li_ail) { | ||
68 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
69 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0); | ||
70 | ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0); | ||
71 | prev_lip = lip; | ||
72 | } | ||
73 | #endif /* XFS_TRANS_DEBUG */ | ||
74 | } | ||
75 | #else /* !DEBUG */ | ||
39 | #define xfs_ail_check(a,l) | 76 | #define xfs_ail_check(a,l) |
40 | #endif /* DEBUG */ | 77 | #endif /* DEBUG */ |
41 | 78 | ||
79 | /* | ||
80 | * Return a pointer to the first item in the AIL. If the AIL is empty, then | ||
81 | * return NULL. | ||
82 | */ | ||
83 | static xfs_log_item_t * | ||
84 | xfs_ail_min( | ||
85 | struct xfs_ail *ailp) | ||
86 | { | ||
87 | if (list_empty(&ailp->xa_ail)) | ||
88 | return NULL; | ||
89 | |||
90 | return list_first_entry(&ailp->xa_ail, xfs_log_item_t, li_ail); | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Return a pointer to the last item in the AIL. If the AIL is empty, then | ||
95 | * return NULL. | ||
96 | */ | ||
97 | static xfs_log_item_t * | ||
98 | xfs_ail_max( | ||
99 | struct xfs_ail *ailp) | ||
100 | { | ||
101 | if (list_empty(&ailp->xa_ail)) | ||
102 | return NULL; | ||
103 | |||
104 | return list_entry(ailp->xa_ail.prev, xfs_log_item_t, li_ail); | ||
105 | } | ||
106 | |||
107 | /* | ||
108 | * Return a pointer to the item which follows the given item in the AIL. If | ||
109 | * the given item is the last item in the list, then return NULL. | ||
110 | */ | ||
111 | static xfs_log_item_t * | ||
112 | xfs_ail_next( | ||
113 | struct xfs_ail *ailp, | ||
114 | xfs_log_item_t *lip) | ||
115 | { | ||
116 | if (lip->li_ail.next == &ailp->xa_ail) | ||
117 | return NULL; | ||
118 | |||
119 | return list_first_entry(&lip->li_ail, xfs_log_item_t, li_ail); | ||
120 | } | ||
42 | 121 | ||
43 | /* | 122 | /* |
44 | * This is called by the log manager code to determine the LSN | 123 | * This is called by the log manager code to determine the LSN of the tail of |
45 | * of the tail of the log. This is exactly the LSN of the first | 124 | * the log. This is exactly the LSN of the first item in the AIL. If the AIL |
46 | * item in the AIL. If the AIL is empty, then this function | 125 | * is empty, then this function returns 0. |
47 | * returns 0. | ||
48 | * | 126 | * |
49 | * We need the AIL lock in order to get a coherent read of the | 127 | * We need the AIL lock in order to get a coherent read of the lsn of the last |
50 | * lsn of the last item in the AIL. | 128 | * item in the AIL. |
51 | */ | 129 | */ |
52 | xfs_lsn_t | 130 | xfs_lsn_t |
53 | xfs_trans_ail_tail( | 131 | xfs_ail_min_lsn( |
54 | struct xfs_ail *ailp) | 132 | struct xfs_ail *ailp) |
55 | { | 133 | { |
56 | xfs_lsn_t lsn; | 134 | xfs_lsn_t lsn = 0; |
57 | xfs_log_item_t *lip; | 135 | xfs_log_item_t *lip; |
58 | 136 | ||
59 | spin_lock(&ailp->xa_lock); | 137 | spin_lock(&ailp->xa_lock); |
60 | lip = xfs_ail_min(ailp); | 138 | lip = xfs_ail_min(ailp); |
61 | if (lip == NULL) { | 139 | if (lip) |
62 | lsn = (xfs_lsn_t)0; | ||
63 | } else { | ||
64 | lsn = lip->li_lsn; | 140 | lsn = lip->li_lsn; |
65 | } | ||
66 | spin_unlock(&ailp->xa_lock); | 141 | spin_unlock(&ailp->xa_lock); |
67 | 142 | ||
68 | return lsn; | 143 | return lsn; |
69 | } | 144 | } |
70 | 145 | ||
71 | /* | 146 | /* |
72 | * xfs_trans_push_ail | 147 | * Return the maximum lsn held in the AIL, or zero if the AIL is empty. |
73 | * | ||
74 | * This routine is called to move the tail of the AIL forward. It does this by | ||
75 | * trying to flush items in the AIL whose lsns are below the given | ||
76 | * threshold_lsn. | ||
77 | * | ||
78 | * the push is run asynchronously in a separate thread, so we return the tail | ||
79 | * of the log right now instead of the tail after the push. This means we will | ||
80 | * either continue right away, or we will sleep waiting on the async thread to | ||
81 | * do its work. | ||
82 | * | ||
83 | * We do this unlocked - we only need to know whether there is anything in the | ||
84 | * AIL at the time we are called. We don't need to access the contents of | ||
85 | * any of the objects, so the lock is not needed. | ||
86 | */ | 148 | */ |
87 | void | 149 | static xfs_lsn_t |
88 | xfs_trans_ail_push( | 150 | xfs_ail_max_lsn( |
89 | struct xfs_ail *ailp, | 151 | struct xfs_ail *ailp) |
90 | xfs_lsn_t threshold_lsn) | ||
91 | { | 152 | { |
92 | xfs_log_item_t *lip; | 153 | xfs_lsn_t lsn = 0; |
154 | xfs_log_item_t *lip; | ||
93 | 155 | ||
94 | lip = xfs_ail_min(ailp); | 156 | spin_lock(&ailp->xa_lock); |
95 | if (lip && !XFS_FORCED_SHUTDOWN(ailp->xa_mount)) { | 157 | lip = xfs_ail_max(ailp); |
96 | if (XFS_LSN_CMP(threshold_lsn, ailp->xa_target) > 0) | 158 | if (lip) |
97 | xfsaild_wakeup(ailp, threshold_lsn); | 159 | lsn = lip->li_lsn; |
98 | } | 160 | spin_unlock(&ailp->xa_lock); |
161 | |||
162 | return lsn; | ||
99 | } | 163 | } |
100 | 164 | ||
101 | /* | 165 | /* |
@@ -236,16 +300,57 @@ out: | |||
236 | } | 300 | } |
237 | 301 | ||
238 | /* | 302 | /* |
239 | * xfsaild_push does the work of pushing on the AIL. Returning a timeout of | 303 | * splice the log item list into the AIL at the given LSN. |
240 | * zero indicates that the caller should sleep until woken. | ||
241 | */ | 304 | */ |
242 | long | 305 | static void |
243 | xfsaild_push( | 306 | xfs_ail_splice( |
244 | struct xfs_ail *ailp, | 307 | struct xfs_ail *ailp, |
245 | xfs_lsn_t *last_lsn) | 308 | struct list_head *list, |
309 | xfs_lsn_t lsn) | ||
246 | { | 310 | { |
247 | long tout = 0; | 311 | xfs_log_item_t *next_lip; |
248 | xfs_lsn_t last_pushed_lsn = *last_lsn; | 312 | |
313 | /* If the list is empty, just insert the item. */ | ||
314 | if (list_empty(&ailp->xa_ail)) { | ||
315 | list_splice(list, &ailp->xa_ail); | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) { | ||
320 | if (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0) | ||
321 | break; | ||
322 | } | ||
323 | |||
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 | } | ||
329 | |||
330 | /* | ||
331 | * Delete the given item from the AIL. Return a pointer to the item. | ||
332 | */ | ||
333 | static void | ||
334 | xfs_ail_delete( | ||
335 | struct xfs_ail *ailp, | ||
336 | xfs_log_item_t *lip) | ||
337 | { | ||
338 | xfs_ail_check(ailp, lip); | ||
339 | list_del(&lip->li_ail); | ||
340 | xfs_trans_ail_cursor_clear(ailp, lip); | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * xfs_ail_worker does the work of pushing on the AIL. It will requeue itself | ||
345 | * to run at a later time if there is more work to do to complete the push. | ||
346 | */ | ||
347 | STATIC void | ||
348 | xfs_ail_worker( | ||
349 | struct work_struct *work) | ||
350 | { | ||
351 | struct xfs_ail *ailp = container_of(to_delayed_work(work), | ||
352 | struct xfs_ail, xa_work); | ||
353 | long tout; | ||
249 | xfs_lsn_t target = ailp->xa_target; | 354 | xfs_lsn_t target = ailp->xa_target; |
250 | xfs_lsn_t lsn; | 355 | xfs_lsn_t lsn; |
251 | xfs_log_item_t *lip; | 356 | xfs_log_item_t *lip; |
@@ -256,15 +361,15 @@ xfsaild_push( | |||
256 | 361 | ||
257 | spin_lock(&ailp->xa_lock); | 362 | spin_lock(&ailp->xa_lock); |
258 | xfs_trans_ail_cursor_init(ailp, cur); | 363 | xfs_trans_ail_cursor_init(ailp, cur); |
259 | lip = xfs_trans_ail_cursor_first(ailp, cur, *last_lsn); | 364 | lip = xfs_trans_ail_cursor_first(ailp, cur, ailp->xa_last_pushed_lsn); |
260 | if (!lip || XFS_FORCED_SHUTDOWN(mp)) { | 365 | if (!lip || XFS_FORCED_SHUTDOWN(mp)) { |
261 | /* | 366 | /* |
262 | * AIL is empty or our push has reached the end. | 367 | * AIL is empty or our push has reached the end. |
263 | */ | 368 | */ |
264 | xfs_trans_ail_cursor_done(ailp, cur); | 369 | xfs_trans_ail_cursor_done(ailp, cur); |
265 | spin_unlock(&ailp->xa_lock); | 370 | spin_unlock(&ailp->xa_lock); |
266 | *last_lsn = 0; | 371 | ailp->xa_last_pushed_lsn = 0; |
267 | return tout; | 372 | return; |
268 | } | 373 | } |
269 | 374 | ||
270 | XFS_STATS_INC(xs_push_ail); | 375 | XFS_STATS_INC(xs_push_ail); |
@@ -301,13 +406,13 @@ xfsaild_push( | |||
301 | case XFS_ITEM_SUCCESS: | 406 | case XFS_ITEM_SUCCESS: |
302 | XFS_STATS_INC(xs_push_ail_success); | 407 | XFS_STATS_INC(xs_push_ail_success); |
303 | IOP_PUSH(lip); | 408 | IOP_PUSH(lip); |
304 | last_pushed_lsn = lsn; | 409 | ailp->xa_last_pushed_lsn = lsn; |
305 | break; | 410 | break; |
306 | 411 | ||
307 | case XFS_ITEM_PUSHBUF: | 412 | case XFS_ITEM_PUSHBUF: |
308 | XFS_STATS_INC(xs_push_ail_pushbuf); | 413 | XFS_STATS_INC(xs_push_ail_pushbuf); |
309 | IOP_PUSHBUF(lip); | 414 | IOP_PUSHBUF(lip); |
310 | last_pushed_lsn = lsn; | 415 | ailp->xa_last_pushed_lsn = lsn; |
311 | push_xfsbufd = 1; | 416 | push_xfsbufd = 1; |
312 | break; | 417 | break; |
313 | 418 | ||
@@ -319,7 +424,7 @@ xfsaild_push( | |||
319 | 424 | ||
320 | case XFS_ITEM_LOCKED: | 425 | case XFS_ITEM_LOCKED: |
321 | XFS_STATS_INC(xs_push_ail_locked); | 426 | XFS_STATS_INC(xs_push_ail_locked); |
322 | last_pushed_lsn = lsn; | 427 | ailp->xa_last_pushed_lsn = lsn; |
323 | stuck++; | 428 | stuck++; |
324 | break; | 429 | break; |
325 | 430 | ||
@@ -374,9 +479,23 @@ xfsaild_push( | |||
374 | wake_up_process(mp->m_ddev_targp->bt_task); | 479 | wake_up_process(mp->m_ddev_targp->bt_task); |
375 | } | 480 | } |
376 | 481 | ||
482 | /* assume we have more work to do in a short while */ | ||
483 | tout = 10; | ||
377 | if (!count) { | 484 | if (!count) { |
378 | /* We're past our target or empty, so idle */ | 485 | /* We're past our target or empty, so idle */ |
379 | last_pushed_lsn = 0; | 486 | ailp->xa_last_pushed_lsn = 0; |
487 | |||
488 | /* | ||
489 | * Check for an updated push target before clearing the | ||
490 | * XFS_AIL_PUSHING_BIT. If the target changed, we've got more | ||
491 | * work to do. Wait a bit longer before starting that work. | ||
492 | */ | ||
493 | smp_rmb(); | ||
494 | if (ailp->xa_target == target) { | ||
495 | clear_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags); | ||
496 | return; | ||
497 | } | ||
498 | tout = 50; | ||
380 | } else if (XFS_LSN_CMP(lsn, target) >= 0) { | 499 | } else if (XFS_LSN_CMP(lsn, target) >= 0) { |
381 | /* | 500 | /* |
382 | * We reached the target so wait a bit longer for I/O to | 501 | * We reached the target so wait a bit longer for I/O to |
@@ -384,7 +503,7 @@ xfsaild_push( | |||
384 | * start the next scan from the start of the AIL. | 503 | * start the next scan from the start of the AIL. |
385 | */ | 504 | */ |
386 | tout = 50; | 505 | tout = 50; |
387 | last_pushed_lsn = 0; | 506 | ailp->xa_last_pushed_lsn = 0; |
388 | } else if ((stuck * 100) / count > 90) { | 507 | } else if ((stuck * 100) / count > 90) { |
389 | /* | 508 | /* |
390 | * Either there is a lot of contention on the AIL or we | 509 | * Either there is a lot of contention on the AIL or we |
@@ -396,14 +515,61 @@ xfsaild_push( | |||
396 | * continuing from where we were. | 515 | * continuing from where we were. |
397 | */ | 516 | */ |
398 | tout = 20; | 517 | tout = 20; |
399 | } else { | ||
400 | /* more to do, but wait a short while before continuing */ | ||
401 | tout = 10; | ||
402 | } | 518 | } |
403 | *last_lsn = last_pushed_lsn; | 519 | |
404 | return tout; | 520 | /* There is more to do, requeue us. */ |
521 | queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, | ||
522 | msecs_to_jiffies(tout)); | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * This routine is called to move the tail of the AIL forward. It does this by | ||
527 | * trying to flush items in the AIL whose lsns are below the given | ||
528 | * threshold_lsn. | ||
529 | * | ||
530 | * The push is run asynchronously in a workqueue, which means the caller needs | ||
531 | * to handle waiting on the async flush for space to become available. | ||
532 | * We don't want to interrupt any push that is in progress, hence we only queue | ||
533 | * work if we set the pushing bit approriately. | ||
534 | * | ||
535 | * We do this unlocked - we only need to know whether there is anything in the | ||
536 | * AIL at the time we are called. We don't need to access the contents of | ||
537 | * any of the objects, so the lock is not needed. | ||
538 | */ | ||
539 | void | ||
540 | xfs_ail_push( | ||
541 | struct xfs_ail *ailp, | ||
542 | xfs_lsn_t threshold_lsn) | ||
543 | { | ||
544 | xfs_log_item_t *lip; | ||
545 | |||
546 | lip = xfs_ail_min(ailp); | ||
547 | if (!lip || XFS_FORCED_SHUTDOWN(ailp->xa_mount) || | ||
548 | XFS_LSN_CMP(threshold_lsn, ailp->xa_target) <= 0) | ||
549 | return; | ||
550 | |||
551 | /* | ||
552 | * Ensure that the new target is noticed in push code before it clears | ||
553 | * the XFS_AIL_PUSHING_BIT. | ||
554 | */ | ||
555 | smp_wmb(); | ||
556 | ailp->xa_target = threshold_lsn; | ||
557 | if (!test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags)) | ||
558 | queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, 0); | ||
405 | } | 559 | } |
406 | 560 | ||
561 | /* | ||
562 | * Push out all items in the AIL immediately | ||
563 | */ | ||
564 | void | ||
565 | xfs_ail_push_all( | ||
566 | struct xfs_ail *ailp) | ||
567 | { | ||
568 | xfs_lsn_t threshold_lsn = xfs_ail_max_lsn(ailp); | ||
569 | |||
570 | if (threshold_lsn) | ||
571 | xfs_ail_push(ailp, threshold_lsn); | ||
572 | } | ||
407 | 573 | ||
408 | /* | 574 | /* |
409 | * This is to be called when an item is unlocked that may have | 575 | * This is to be called when an item is unlocked that may have |
@@ -615,7 +781,6 @@ xfs_trans_ail_init( | |||
615 | xfs_mount_t *mp) | 781 | xfs_mount_t *mp) |
616 | { | 782 | { |
617 | struct xfs_ail *ailp; | 783 | struct xfs_ail *ailp; |
618 | int error; | ||
619 | 784 | ||
620 | ailp = kmem_zalloc(sizeof(struct xfs_ail), KM_MAYFAIL); | 785 | ailp = kmem_zalloc(sizeof(struct xfs_ail), KM_MAYFAIL); |
621 | if (!ailp) | 786 | if (!ailp) |
@@ -624,15 +789,9 @@ xfs_trans_ail_init( | |||
624 | ailp->xa_mount = mp; | 789 | ailp->xa_mount = mp; |
625 | INIT_LIST_HEAD(&ailp->xa_ail); | 790 | INIT_LIST_HEAD(&ailp->xa_ail); |
626 | spin_lock_init(&ailp->xa_lock); | 791 | spin_lock_init(&ailp->xa_lock); |
627 | error = xfsaild_start(ailp); | 792 | INIT_DELAYED_WORK(&ailp->xa_work, xfs_ail_worker); |
628 | if (error) | ||
629 | goto out_free_ailp; | ||
630 | mp->m_ail = ailp; | 793 | mp->m_ail = ailp; |
631 | return 0; | 794 | return 0; |
632 | |||
633 | out_free_ailp: | ||
634 | kmem_free(ailp); | ||
635 | return error; | ||
636 | } | 795 | } |
637 | 796 | ||
638 | void | 797 | void |
@@ -641,124 +800,6 @@ xfs_trans_ail_destroy( | |||
641 | { | 800 | { |
642 | struct xfs_ail *ailp = mp->m_ail; | 801 | struct xfs_ail *ailp = mp->m_ail; |
643 | 802 | ||
644 | xfsaild_stop(ailp); | 803 | cancel_delayed_work_sync(&ailp->xa_work); |
645 | kmem_free(ailp); | 804 | kmem_free(ailp); |
646 | } | 805 | } |
647 | |||
648 | /* | ||
649 | * splice the log item list into the AIL at the given LSN. | ||
650 | */ | ||
651 | STATIC void | ||
652 | xfs_ail_splice( | ||
653 | struct xfs_ail *ailp, | ||
654 | struct list_head *list, | ||
655 | xfs_lsn_t lsn) | ||
656 | { | ||
657 | xfs_log_item_t *next_lip; | ||
658 | |||
659 | /* | ||
660 | * If the list is empty, just insert the item. | ||
661 | */ | ||
662 | if (list_empty(&ailp->xa_ail)) { | ||
663 | list_splice(list, &ailp->xa_ail); | ||
664 | return; | ||
665 | } | ||
666 | |||
667 | list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) { | ||
668 | if (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0) | ||
669 | break; | ||
670 | } | ||
671 | |||
672 | ASSERT((&next_lip->li_ail == &ailp->xa_ail) || | ||
673 | (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0)); | ||
674 | |||
675 | list_splice_init(list, &next_lip->li_ail); | ||
676 | return; | ||
677 | } | ||
678 | |||
679 | /* | ||
680 | * Delete the given item from the AIL. Return a pointer to the item. | ||
681 | */ | ||
682 | STATIC void | ||
683 | xfs_ail_delete( | ||
684 | struct xfs_ail *ailp, | ||
685 | xfs_log_item_t *lip) | ||
686 | { | ||
687 | xfs_ail_check(ailp, lip); | ||
688 | list_del(&lip->li_ail); | ||
689 | xfs_trans_ail_cursor_clear(ailp, lip); | ||
690 | } | ||
691 | |||
692 | /* | ||
693 | * Return a pointer to the first item in the AIL. | ||
694 | * If the AIL is empty, then return NULL. | ||
695 | */ | ||
696 | STATIC xfs_log_item_t * | ||
697 | xfs_ail_min( | ||
698 | struct xfs_ail *ailp) | ||
699 | { | ||
700 | if (list_empty(&ailp->xa_ail)) | ||
701 | return NULL; | ||
702 | |||
703 | return list_first_entry(&ailp->xa_ail, xfs_log_item_t, li_ail); | ||
704 | } | ||
705 | |||
706 | /* | ||
707 | * Return a pointer to the item which follows | ||
708 | * the given item in the AIL. If the given item | ||
709 | * is the last item in the list, then return NULL. | ||
710 | */ | ||
711 | STATIC xfs_log_item_t * | ||
712 | xfs_ail_next( | ||
713 | struct xfs_ail *ailp, | ||
714 | xfs_log_item_t *lip) | ||
715 | { | ||
716 | if (lip->li_ail.next == &ailp->xa_ail) | ||
717 | return NULL; | ||
718 | |||
719 | return list_first_entry(&lip->li_ail, xfs_log_item_t, li_ail); | ||
720 | } | ||
721 | |||
722 | #ifdef DEBUG | ||
723 | /* | ||
724 | * Check that the list is sorted as it should be. | ||
725 | */ | ||
726 | STATIC void | ||
727 | xfs_ail_check( | ||
728 | struct xfs_ail *ailp, | ||
729 | xfs_log_item_t *lip) | ||
730 | { | ||
731 | xfs_log_item_t *prev_lip; | ||
732 | |||
733 | if (list_empty(&ailp->xa_ail)) | ||
734 | return; | ||
735 | |||
736 | /* | ||
737 | * Check the next and previous entries are valid. | ||
738 | */ | ||
739 | ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0); | ||
740 | prev_lip = list_entry(lip->li_ail.prev, xfs_log_item_t, li_ail); | ||
741 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
742 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0); | ||
743 | |||
744 | prev_lip = list_entry(lip->li_ail.next, xfs_log_item_t, li_ail); | ||
745 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
746 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) >= 0); | ||
747 | |||
748 | |||
749 | #ifdef XFS_TRANS_DEBUG | ||
750 | /* | ||
751 | * Walk the list checking lsn ordering, and that every entry has the | ||
752 | * XFS_LI_IN_AIL flag set. This is really expensive, so only do it | ||
753 | * when specifically debugging the transaction subsystem. | ||
754 | */ | ||
755 | prev_lip = list_entry(&ailp->xa_ail, xfs_log_item_t, li_ail); | ||
756 | list_for_each_entry(lip, &ailp->xa_ail, li_ail) { | ||
757 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
758 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0); | ||
759 | ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0); | ||
760 | prev_lip = lip; | ||
761 | } | ||
762 | #endif /* XFS_TRANS_DEBUG */ | ||
763 | } | ||
764 | #endif /* DEBUG */ | ||