diff options
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_inode_item.c | 90 | ||||
-rw-r--r-- | fs/xfs/xfs_trans_ail.c | 73 | ||||
-rw-r--r-- | fs/xfs/xfs_trans_priv.h | 4 |
3 files changed, 151 insertions, 16 deletions
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 7c8d30c453c3..fd4f398bd6f1 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c | |||
@@ -842,15 +842,64 @@ xfs_inode_item_destroy( | |||
842 | * flushed to disk. It is responsible for removing the inode item | 842 | * flushed to disk. It is responsible for removing the inode item |
843 | * from the AIL if it has not been re-logged, and unlocking the inode's | 843 | * from the AIL if it has not been re-logged, and unlocking the inode's |
844 | * flush lock. | 844 | * flush lock. |
845 | * | ||
846 | * To reduce AIL lock traffic as much as possible, we scan the buffer log item | ||
847 | * list for other inodes that will run this function. We remove them from the | ||
848 | * buffer list so we can process all the inode IO completions in one AIL lock | ||
849 | * traversal. | ||
845 | */ | 850 | */ |
846 | void | 851 | void |
847 | xfs_iflush_done( | 852 | xfs_iflush_done( |
848 | struct xfs_buf *bp, | 853 | struct xfs_buf *bp, |
849 | struct xfs_log_item *lip) | 854 | struct xfs_log_item *lip) |
850 | { | 855 | { |
851 | struct xfs_inode_log_item *iip = INODE_ITEM(lip); | 856 | struct xfs_inode_log_item *iip; |
852 | xfs_inode_t *ip = iip->ili_inode; | 857 | struct xfs_log_item *blip; |
858 | struct xfs_log_item *next; | ||
859 | struct xfs_log_item *prev; | ||
853 | struct xfs_ail *ailp = lip->li_ailp; | 860 | struct xfs_ail *ailp = lip->li_ailp; |
861 | int need_ail = 0; | ||
862 | |||
863 | /* | ||
864 | * Scan the buffer IO completions for other inodes being completed and | ||
865 | * attach them to the current inode log item. | ||
866 | */ | ||
867 | blip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *); | ||
868 | prev = NULL; | ||
869 | while (blip != NULL) { | ||
870 | if (lip->li_cb != xfs_iflush_done) { | ||
871 | prev = blip; | ||
872 | blip = blip->li_bio_list; | ||
873 | continue; | ||
874 | } | ||
875 | |||
876 | /* remove from list */ | ||
877 | next = blip->li_bio_list; | ||
878 | if (!prev) { | ||
879 | XFS_BUF_SET_FSPRIVATE(bp, next); | ||
880 | } else { | ||
881 | prev->li_bio_list = next; | ||
882 | } | ||
883 | |||
884 | /* add to current list */ | ||
885 | blip->li_bio_list = lip->li_bio_list; | ||
886 | lip->li_bio_list = blip; | ||
887 | |||
888 | /* | ||
889 | * while we have the item, do the unlocked check for needing | ||
890 | * the AIL lock. | ||
891 | */ | ||
892 | iip = INODE_ITEM(blip); | ||
893 | if (iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn) | ||
894 | need_ail++; | ||
895 | |||
896 | blip = next; | ||
897 | } | ||
898 | |||
899 | /* make sure we capture the state of the initial inode. */ | ||
900 | iip = INODE_ITEM(lip); | ||
901 | if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) | ||
902 | need_ail++; | ||
854 | 903 | ||
855 | /* | 904 | /* |
856 | * We only want to pull the item from the AIL if it is | 905 | * We only want to pull the item from the AIL if it is |
@@ -861,28 +910,37 @@ xfs_iflush_done( | |||
861 | * the lock since it's cheaper, and then we recheck while | 910 | * the lock since it's cheaper, and then we recheck while |
862 | * holding the lock before removing the inode from the AIL. | 911 | * holding the lock before removing the inode from the AIL. |
863 | */ | 912 | */ |
864 | if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) { | 913 | if (need_ail) { |
914 | struct xfs_log_item *log_items[need_ail]; | ||
915 | int i = 0; | ||
865 | spin_lock(&ailp->xa_lock); | 916 | spin_lock(&ailp->xa_lock); |
866 | if (lip->li_lsn == iip->ili_flush_lsn) { | 917 | for (blip = lip; blip; blip = blip->li_bio_list) { |
867 | /* xfs_trans_ail_delete() drops the AIL lock. */ | 918 | iip = INODE_ITEM(blip); |
868 | xfs_trans_ail_delete(ailp, lip); | 919 | if (iip->ili_logged && |
869 | } else { | 920 | blip->li_lsn == iip->ili_flush_lsn) { |
870 | spin_unlock(&ailp->xa_lock); | 921 | log_items[i++] = blip; |
922 | } | ||
923 | ASSERT(i <= need_ail); | ||
871 | } | 924 | } |
925 | /* xfs_trans_ail_delete_bulk() drops the AIL lock. */ | ||
926 | xfs_trans_ail_delete_bulk(ailp, log_items, i); | ||
872 | } | 927 | } |
873 | 928 | ||
874 | iip->ili_logged = 0; | ||
875 | 929 | ||
876 | /* | 930 | /* |
877 | * Clear the ili_last_fields bits now that we know that the | 931 | * clean up and unlock the flush lock now we are done. We can clear the |
878 | * data corresponding to them is safely on disk. | 932 | * ili_last_fields bits now that we know that the data corresponding to |
933 | * them is safely on disk. | ||
879 | */ | 934 | */ |
880 | iip->ili_last_fields = 0; | 935 | for (blip = lip; blip; blip = next) { |
936 | next = blip->li_bio_list; | ||
937 | blip->li_bio_list = NULL; | ||
881 | 938 | ||
882 | /* | 939 | iip = INODE_ITEM(blip); |
883 | * Release the inode's flush lock since we're done with it. | 940 | iip->ili_logged = 0; |
884 | */ | 941 | iip->ili_last_fields = 0; |
885 | xfs_ifunlock(ip); | 942 | xfs_ifunlock(iip->ili_inode); |
943 | } | ||
886 | } | 944 | } |
887 | 945 | ||
888 | /* | 946 | /* |
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index fe991a76bf14..218f96861c80 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c | |||
@@ -639,6 +639,79 @@ xfs_trans_ail_delete( | |||
639 | } | 639 | } |
640 | } | 640 | } |
641 | 641 | ||
642 | /* | ||
643 | * xfs_trans_ail_delete_bulk - remove multiple log items from the AIL | ||
644 | * | ||
645 | * @xfs_trans_ail_delete_bulk takes an array of log items that all need to | ||
646 | * removed from the AIL. The caller is already holding the AIL lock, and done | ||
647 | * all the checks necessary to ensure the items passed in via @log_items are | ||
648 | * ready for deletion. This includes checking that the items are in the AIL. | ||
649 | * | ||
650 | * For each log item to be removed, unlink it from the AIL, clear the IN_AIL | ||
651 | * flag from the item and reset the item's lsn to 0. If we remove the first | ||
652 | * item in the AIL, update the log tail to match the new minimum LSN in the | ||
653 | * AIL. | ||
654 | * | ||
655 | * This function will not drop the AIL lock until all items are removed from | ||
656 | * the AIL to minimise the amount of lock traffic on the AIL. This does not | ||
657 | * greatly increase the AIL hold time, but does significantly reduce the amount | ||
658 | * of traffic on the lock, especially during IO completion. | ||
659 | * | ||
660 | * This function must be called with the AIL lock held. The lock is dropped | ||
661 | * before returning. | ||
662 | */ | ||
663 | void | ||
664 | xfs_trans_ail_delete_bulk( | ||
665 | struct xfs_ail *ailp, | ||
666 | struct xfs_log_item **log_items, | ||
667 | int nr_items) __releases(ailp->xa_lock) | ||
668 | { | ||
669 | xfs_log_item_t *mlip; | ||
670 | xfs_lsn_t tail_lsn; | ||
671 | int mlip_changed = 0; | ||
672 | int i; | ||
673 | |||
674 | mlip = xfs_ail_min(ailp); | ||
675 | |||
676 | for (i = 0; i < nr_items; i++) { | ||
677 | struct xfs_log_item *lip = log_items[i]; | ||
678 | if (!(lip->li_flags & XFS_LI_IN_AIL)) { | ||
679 | struct xfs_mount *mp = ailp->xa_mount; | ||
680 | |||
681 | spin_unlock(&ailp->xa_lock); | ||
682 | if (!XFS_FORCED_SHUTDOWN(mp)) { | ||
683 | xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp, | ||
684 | "%s: attempting to delete a log item that is not in the AIL", | ||
685 | __func__); | ||
686 | xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); | ||
687 | } | ||
688 | return; | ||
689 | } | ||
690 | |||
691 | xfs_ail_delete(ailp, lip); | ||
692 | lip->li_flags &= ~XFS_LI_IN_AIL; | ||
693 | lip->li_lsn = 0; | ||
694 | if (mlip == lip) | ||
695 | mlip_changed = 1; | ||
696 | } | ||
697 | |||
698 | if (!mlip_changed) { | ||
699 | spin_unlock(&ailp->xa_lock); | ||
700 | return; | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * It is not safe to access mlip after the AIL lock is dropped, so we | ||
705 | * must get a copy of li_lsn before we do so. This is especially | ||
706 | * important on 32-bit platforms where accessing and updating 64-bit | ||
707 | * values like li_lsn is not atomic. It is possible we've emptied the | ||
708 | * AIL here, so if that is the case, pass an LSN of 0 to the tail move. | ||
709 | */ | ||
710 | mlip = xfs_ail_min(ailp); | ||
711 | tail_lsn = mlip ? mlip->li_lsn : 0; | ||
712 | spin_unlock(&ailp->xa_lock); | ||
713 | xfs_log_move_tail(ailp->xa_mount, tail_lsn); | ||
714 | } | ||
642 | 715 | ||
643 | 716 | ||
644 | /* | 717 | /* |
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h index e039729186e9..246ca4dcb5c4 100644 --- a/fs/xfs/xfs_trans_priv.h +++ b/fs/xfs/xfs_trans_priv.h | |||
@@ -85,6 +85,10 @@ void xfs_trans_ail_update_bulk(struct xfs_ail *ailp, | |||
85 | void xfs_trans_ail_delete(struct xfs_ail *ailp, | 85 | void xfs_trans_ail_delete(struct xfs_ail *ailp, |
86 | struct xfs_log_item *lip) | 86 | struct xfs_log_item *lip) |
87 | __releases(ailp->xa_lock); | 87 | __releases(ailp->xa_lock); |
88 | void xfs_trans_ail_delete_bulk(struct xfs_ail *ailp, | ||
89 | struct xfs_log_item **log_items, | ||
90 | int nr_items) | ||
91 | __releases(ailp->xa_lock); | ||
88 | void xfs_trans_ail_push(struct xfs_ail *, xfs_lsn_t); | 92 | void xfs_trans_ail_push(struct xfs_ail *, xfs_lsn_t); |
89 | void xfs_trans_unlocked_item(struct xfs_ail *, | 93 | void xfs_trans_unlocked_item(struct xfs_ail *, |
90 | xfs_log_item_t *); | 94 | xfs_log_item_t *); |