diff options
| -rw-r--r-- | fs/xfs/xfs_trans_ail.c | 254 |
1 files changed, 118 insertions, 136 deletions
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index cb3aeac929b..8012bfbc6dc 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c | |||
| @@ -30,41 +30,100 @@ | |||
| 30 | 30 | ||
| 31 | struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ | 31 | struct workqueue_struct *xfs_ail_wq; /* AIL workqueue */ |
| 32 | 32 | ||
| 33 | STATIC void xfs_ail_splice(struct xfs_ail *, struct list_head *, xfs_lsn_t); | ||
| 34 | STATIC void xfs_ail_delete(struct xfs_ail *, xfs_log_item_t *); | ||
| 35 | STATIC xfs_log_item_t * xfs_ail_min(struct xfs_ail *); | ||
| 36 | STATIC xfs_log_item_t * xfs_ail_next(struct xfs_ail *, xfs_log_item_t *); | ||
| 37 | |||
| 38 | #ifdef DEBUG | 33 | #ifdef DEBUG |
| 39 | STATIC void xfs_ail_check(struct xfs_ail *, xfs_log_item_t *); | 34 | /* |
| 40 | #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 */ | ||
| 41 | #define xfs_ail_check(a,l) | 76 | #define xfs_ail_check(a,l) |
| 42 | #endif /* DEBUG */ | 77 | #endif /* DEBUG */ |
| 43 | 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 item which follows the given item in the AIL. If | ||
| 95 | * the given item is the last item in the list, then return NULL. | ||
| 96 | */ | ||
| 97 | static xfs_log_item_t * | ||
| 98 | xfs_ail_next( | ||
| 99 | struct xfs_ail *ailp, | ||
| 100 | xfs_log_item_t *lip) | ||
| 101 | { | ||
| 102 | if (lip->li_ail.next == &ailp->xa_ail) | ||
| 103 | return NULL; | ||
| 104 | |||
| 105 | return list_first_entry(&lip->li_ail, xfs_log_item_t, li_ail); | ||
| 106 | } | ||
| 44 | 107 | ||
| 45 | /* | 108 | /* |
| 46 | * This is called by the log manager code to determine the LSN | 109 | * This is called by the log manager code to determine the LSN of the tail of |
| 47 | * of the tail of the log. This is exactly the LSN of the first | 110 | * the log. This is exactly the LSN of the first item in the AIL. If the AIL |
| 48 | * item in the AIL. If the AIL is empty, then this function | 111 | * is empty, then this function returns 0. |
| 49 | * returns 0. | ||
| 50 | * | 112 | * |
| 51 | * We need the AIL lock in order to get a coherent read of the | 113 | * We need the AIL lock in order to get a coherent read of the lsn of the last |
| 52 | * lsn of the last item in the AIL. | 114 | * item in the AIL. |
| 53 | */ | 115 | */ |
| 54 | xfs_lsn_t | 116 | xfs_lsn_t |
| 55 | xfs_trans_ail_tail( | 117 | xfs_trans_ail_tail( |
| 56 | struct xfs_ail *ailp) | 118 | struct xfs_ail *ailp) |
| 57 | { | 119 | { |
| 58 | xfs_lsn_t lsn; | 120 | xfs_lsn_t lsn = 0; |
| 59 | xfs_log_item_t *lip; | 121 | xfs_log_item_t *lip; |
| 60 | 122 | ||
| 61 | spin_lock(&ailp->xa_lock); | 123 | spin_lock(&ailp->xa_lock); |
| 62 | lip = xfs_ail_min(ailp); | 124 | lip = xfs_ail_min(ailp); |
| 63 | if (lip == NULL) { | 125 | if (lip) |
| 64 | lsn = (xfs_lsn_t)0; | ||
| 65 | } else { | ||
| 66 | lsn = lip->li_lsn; | 126 | lsn = lip->li_lsn; |
| 67 | } | ||
| 68 | spin_unlock(&ailp->xa_lock); | 127 | spin_unlock(&ailp->xa_lock); |
| 69 | 128 | ||
| 70 | return lsn; | 129 | return lsn; |
| @@ -208,6 +267,47 @@ out: | |||
| 208 | } | 267 | } |
| 209 | 268 | ||
| 210 | /* | 269 | /* |
| 270 | * splice the log item list into the AIL at the given LSN. | ||
| 271 | */ | ||
| 272 | static void | ||
| 273 | xfs_ail_splice( | ||
| 274 | struct xfs_ail *ailp, | ||
| 275 | struct list_head *list, | ||
| 276 | xfs_lsn_t lsn) | ||
| 277 | { | ||
| 278 | xfs_log_item_t *next_lip; | ||
| 279 | |||
| 280 | /* If the list is empty, just insert the item. */ | ||
| 281 | if (list_empty(&ailp->xa_ail)) { | ||
| 282 | list_splice(list, &ailp->xa_ail); | ||
| 283 | return; | ||
| 284 | } | ||
| 285 | |||
| 286 | list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) { | ||
| 287 | if (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0) | ||
| 288 | break; | ||
| 289 | } | ||
| 290 | |||
| 291 | ASSERT(&next_lip->li_ail == &ailp->xa_ail || | ||
| 292 | XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0); | ||
| 293 | |||
| 294 | list_splice_init(list, &next_lip->li_ail); | ||
| 295 | } | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Delete the given item from the AIL. Return a pointer to the item. | ||
| 299 | */ | ||
| 300 | static void | ||
| 301 | xfs_ail_delete( | ||
| 302 | struct xfs_ail *ailp, | ||
| 303 | xfs_log_item_t *lip) | ||
| 304 | { | ||
| 305 | xfs_ail_check(ailp, lip); | ||
| 306 | list_del(&lip->li_ail); | ||
| 307 | xfs_trans_ail_cursor_clear(ailp, lip); | ||
| 308 | } | ||
| 309 | |||
| 310 | /* | ||
| 211 | * xfs_ail_worker does the work of pushing on the AIL. It will requeue itself | 311 | * xfs_ail_worker does the work of pushing on the AIL. It will requeue itself |
| 212 | * to run at a later time if there is more work to do to complete the push. | 312 | * to run at a later time if there is more work to do to complete the push. |
| 213 | */ | 313 | */ |
| @@ -657,121 +757,3 @@ xfs_trans_ail_destroy( | |||
| 657 | cancel_delayed_work_sync(&ailp->xa_work); | 757 | cancel_delayed_work_sync(&ailp->xa_work); |
| 658 | kmem_free(ailp); | 758 | kmem_free(ailp); |
| 659 | } | 759 | } |
| 660 | |||
| 661 | /* | ||
| 662 | * splice the log item list into the AIL at the given LSN. | ||
| 663 | */ | ||
| 664 | STATIC void | ||
| 665 | xfs_ail_splice( | ||
| 666 | struct xfs_ail *ailp, | ||
| 667 | struct list_head *list, | ||
| 668 | xfs_lsn_t lsn) | ||
| 669 | { | ||
| 670 | xfs_log_item_t *next_lip; | ||
| 671 | |||
| 672 | /* | ||
| 673 | * If the list is empty, just insert the item. | ||
| 674 | */ | ||
| 675 | if (list_empty(&ailp->xa_ail)) { | ||
| 676 | list_splice(list, &ailp->xa_ail); | ||
| 677 | return; | ||
| 678 | } | ||
| 679 | |||
| 680 | list_for_each_entry_reverse(next_lip, &ailp->xa_ail, li_ail) { | ||
| 681 | if (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0) | ||
| 682 | break; | ||
| 683 | } | ||
| 684 | |||
| 685 | ASSERT((&next_lip->li_ail == &ailp->xa_ail) || | ||
| 686 | (XFS_LSN_CMP(next_lip->li_lsn, lsn) <= 0)); | ||
| 687 | |||
| 688 | list_splice_init(list, &next_lip->li_ail); | ||
| 689 | return; | ||
| 690 | } | ||
| 691 | |||
| 692 | /* | ||
| 693 | * Delete the given item from the AIL. Return a pointer to the item. | ||
| 694 | */ | ||
| 695 | STATIC void | ||
| 696 | xfs_ail_delete( | ||
| 697 | struct xfs_ail *ailp, | ||
| 698 | xfs_log_item_t *lip) | ||
| 699 | { | ||
| 700 | xfs_ail_check(ailp, lip); | ||
| 701 | list_del(&lip->li_ail); | ||
| 702 | xfs_trans_ail_cursor_clear(ailp, lip); | ||
| 703 | } | ||
| 704 | |||
| 705 | /* | ||
| 706 | * Return a pointer to the first item in the AIL. | ||
| 707 | * If the AIL is empty, then return NULL. | ||
| 708 | */ | ||
| 709 | STATIC xfs_log_item_t * | ||
| 710 | xfs_ail_min( | ||
| 711 | struct xfs_ail *ailp) | ||
| 712 | { | ||
| 713 | if (list_empty(&ailp->xa_ail)) | ||
| 714 | return NULL; | ||
| 715 | |||
| 716 | return list_first_entry(&ailp->xa_ail, xfs_log_item_t, li_ail); | ||
| 717 | } | ||
| 718 | |||
| 719 | /* | ||
| 720 | * Return a pointer to the item which follows | ||
| 721 | * the given item in the AIL. If the given item | ||
| 722 | * is the last item in the list, then return NULL. | ||
| 723 | */ | ||
| 724 | STATIC xfs_log_item_t * | ||
| 725 | xfs_ail_next( | ||
| 726 | struct xfs_ail *ailp, | ||
| 727 | xfs_log_item_t *lip) | ||
| 728 | { | ||
| 729 | if (lip->li_ail.next == &ailp->xa_ail) | ||
| 730 | return NULL; | ||
| 731 | |||
| 732 | return list_first_entry(&lip->li_ail, xfs_log_item_t, li_ail); | ||
| 733 | } | ||
| 734 | |||
| 735 | #ifdef DEBUG | ||
| 736 | /* | ||
| 737 | * Check that the list is sorted as it should be. | ||
| 738 | */ | ||
| 739 | STATIC void | ||
| 740 | xfs_ail_check( | ||
| 741 | struct xfs_ail *ailp, | ||
| 742 | xfs_log_item_t *lip) | ||
| 743 | { | ||
| 744 | xfs_log_item_t *prev_lip; | ||
| 745 | |||
| 746 | if (list_empty(&ailp->xa_ail)) | ||
| 747 | return; | ||
| 748 | |||
| 749 | /* | ||
| 750 | * Check the next and previous entries are valid. | ||
| 751 | */ | ||
| 752 | ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0); | ||
| 753 | prev_lip = list_entry(lip->li_ail.prev, xfs_log_item_t, li_ail); | ||
| 754 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
| 755 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0); | ||
| 756 | |||
| 757 | prev_lip = list_entry(lip->li_ail.next, xfs_log_item_t, li_ail); | ||
| 758 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
| 759 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) >= 0); | ||
| 760 | |||
| 761 | |||
| 762 | #ifdef XFS_TRANS_DEBUG | ||
| 763 | /* | ||
| 764 | * Walk the list checking lsn ordering, and that every entry has the | ||
| 765 | * XFS_LI_IN_AIL flag set. This is really expensive, so only do it | ||
| 766 | * when specifically debugging the transaction subsystem. | ||
| 767 | */ | ||
| 768 | prev_lip = list_entry(&ailp->xa_ail, xfs_log_item_t, li_ail); | ||
| 769 | list_for_each_entry(lip, &ailp->xa_ail, li_ail) { | ||
| 770 | if (&prev_lip->li_ail != &ailp->xa_ail) | ||
| 771 | ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0); | ||
| 772 | ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0); | ||
| 773 | prev_lip = lip; | ||
| 774 | } | ||
| 775 | #endif /* XFS_TRANS_DEBUG */ | ||
| 776 | } | ||
| 777 | #endif /* DEBUG */ | ||
