diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2018-08-10 01:43:04 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-08-10 14:44:31 -0400 |
commit | 13942aa94a8b5df662d93c42c307b2f50cbe88b0 (patch) | |
tree | 38276489cfc812cd420471d86bc4cf1a245e7f65 /fs/xfs | |
parent | 0e93d3f43ec7d3308bff25ce1be81d46330168c9 (diff) |
xfs: repair the AGI
Rebuild the AGI header items with some help from the rmapbt.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/scrub/agheader_repair.c | 217 | ||||
-rw-r--r-- | fs/xfs/scrub/repair.h | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/scrub.c | 2 |
3 files changed, 220 insertions, 1 deletions
diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 9ce302360bbb..f7568a4b5fe5 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c | |||
@@ -714,3 +714,220 @@ err: | |||
714 | xfs_bitmap_destroy(&agfl_extents); | 714 | xfs_bitmap_destroy(&agfl_extents); |
715 | return error; | 715 | return error; |
716 | } | 716 | } |
717 | |||
718 | /* AGI */ | ||
719 | |||
720 | /* | ||
721 | * Offset within the xrep_find_ag_btree array for each btree type. Avoid the | ||
722 | * XFS_BTNUM_ names here to avoid creating a sparse array. | ||
723 | */ | ||
724 | enum { | ||
725 | XREP_AGI_INOBT = 0, | ||
726 | XREP_AGI_FINOBT, | ||
727 | XREP_AGI_END, | ||
728 | XREP_AGI_MAX | ||
729 | }; | ||
730 | |||
731 | /* | ||
732 | * Given the inode btree roots described by *fab, find the roots, check them | ||
733 | * for sanity, and pass the root data back out via *fab. | ||
734 | */ | ||
735 | STATIC int | ||
736 | xrep_agi_find_btrees( | ||
737 | struct xfs_scrub *sc, | ||
738 | struct xrep_find_ag_btree *fab) | ||
739 | { | ||
740 | struct xfs_buf *agf_bp; | ||
741 | struct xfs_mount *mp = sc->mp; | ||
742 | int error; | ||
743 | |||
744 | /* Read the AGF. */ | ||
745 | error = xfs_alloc_read_agf(mp, sc->tp, sc->sa.agno, 0, &agf_bp); | ||
746 | if (error) | ||
747 | return error; | ||
748 | if (!agf_bp) | ||
749 | return -ENOMEM; | ||
750 | |||
751 | /* Find the btree roots. */ | ||
752 | error = xrep_find_ag_btree_roots(sc, agf_bp, fab, NULL); | ||
753 | if (error) | ||
754 | return error; | ||
755 | |||
756 | /* We must find the inobt root. */ | ||
757 | if (!xrep_check_btree_root(sc, &fab[XREP_AGI_INOBT])) | ||
758 | return -EFSCORRUPTED; | ||
759 | |||
760 | /* We must find the finobt root if that feature is enabled. */ | ||
761 | if (xfs_sb_version_hasfinobt(&mp->m_sb) && | ||
762 | !xrep_check_btree_root(sc, &fab[XREP_AGI_FINOBT])) | ||
763 | return -EFSCORRUPTED; | ||
764 | |||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | /* | ||
769 | * Reinitialize the AGI header, making an in-core copy of the old contents so | ||
770 | * that we know which in-core state needs to be reinitialized. | ||
771 | */ | ||
772 | STATIC void | ||
773 | xrep_agi_init_header( | ||
774 | struct xfs_scrub *sc, | ||
775 | struct xfs_buf *agi_bp, | ||
776 | struct xfs_agi *old_agi) | ||
777 | { | ||
778 | struct xfs_agi *agi = XFS_BUF_TO_AGI(agi_bp); | ||
779 | struct xfs_mount *mp = sc->mp; | ||
780 | |||
781 | memcpy(old_agi, agi, sizeof(*old_agi)); | ||
782 | memset(agi, 0, BBTOB(agi_bp->b_length)); | ||
783 | agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC); | ||
784 | agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION); | ||
785 | agi->agi_seqno = cpu_to_be32(sc->sa.agno); | ||
786 | agi->agi_length = cpu_to_be32(xfs_ag_block_count(mp, sc->sa.agno)); | ||
787 | agi->agi_newino = cpu_to_be32(NULLAGINO); | ||
788 | agi->agi_dirino = cpu_to_be32(NULLAGINO); | ||
789 | if (xfs_sb_version_hascrc(&mp->m_sb)) | ||
790 | uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid); | ||
791 | |||
792 | /* We don't know how to fix the unlinked list yet. */ | ||
793 | memcpy(&agi->agi_unlinked, &old_agi->agi_unlinked, | ||
794 | sizeof(agi->agi_unlinked)); | ||
795 | |||
796 | /* Mark the incore AGF data stale until we're done fixing things. */ | ||
797 | ASSERT(sc->sa.pag->pagi_init); | ||
798 | sc->sa.pag->pagi_init = 0; | ||
799 | } | ||
800 | |||
801 | /* Set btree root information in an AGI. */ | ||
802 | STATIC void | ||
803 | xrep_agi_set_roots( | ||
804 | struct xfs_scrub *sc, | ||
805 | struct xfs_agi *agi, | ||
806 | struct xrep_find_ag_btree *fab) | ||
807 | { | ||
808 | agi->agi_root = cpu_to_be32(fab[XREP_AGI_INOBT].root); | ||
809 | agi->agi_level = cpu_to_be32(fab[XREP_AGI_INOBT].height); | ||
810 | |||
811 | if (xfs_sb_version_hasfinobt(&sc->mp->m_sb)) { | ||
812 | agi->agi_free_root = cpu_to_be32(fab[XREP_AGI_FINOBT].root); | ||
813 | agi->agi_free_level = cpu_to_be32(fab[XREP_AGI_FINOBT].height); | ||
814 | } | ||
815 | } | ||
816 | |||
817 | /* Update the AGI counters. */ | ||
818 | STATIC int | ||
819 | xrep_agi_calc_from_btrees( | ||
820 | struct xfs_scrub *sc, | ||
821 | struct xfs_buf *agi_bp) | ||
822 | { | ||
823 | struct xfs_btree_cur *cur; | ||
824 | struct xfs_agi *agi = XFS_BUF_TO_AGI(agi_bp); | ||
825 | struct xfs_mount *mp = sc->mp; | ||
826 | xfs_agino_t count; | ||
827 | xfs_agino_t freecount; | ||
828 | int error; | ||
829 | |||
830 | cur = xfs_inobt_init_cursor(mp, sc->tp, agi_bp, sc->sa.agno, | ||
831 | XFS_BTNUM_INO); | ||
832 | error = xfs_ialloc_count_inodes(cur, &count, &freecount); | ||
833 | if (error) | ||
834 | goto err; | ||
835 | xfs_btree_del_cursor(cur, error); | ||
836 | |||
837 | agi->agi_count = cpu_to_be32(count); | ||
838 | agi->agi_freecount = cpu_to_be32(freecount); | ||
839 | return 0; | ||
840 | err: | ||
841 | xfs_btree_del_cursor(cur, error); | ||
842 | return error; | ||
843 | } | ||
844 | |||
845 | /* Trigger reinitialization of the in-core data. */ | ||
846 | STATIC int | ||
847 | xrep_agi_commit_new( | ||
848 | struct xfs_scrub *sc, | ||
849 | struct xfs_buf *agi_bp) | ||
850 | { | ||
851 | struct xfs_perag *pag; | ||
852 | struct xfs_agi *agi = XFS_BUF_TO_AGI(agi_bp); | ||
853 | |||
854 | /* Trigger inode count recalculation */ | ||
855 | xfs_force_summary_recalc(sc->mp); | ||
856 | |||
857 | /* Write this to disk. */ | ||
858 | xfs_trans_buf_set_type(sc->tp, agi_bp, XFS_BLFT_AGI_BUF); | ||
859 | xfs_trans_log_buf(sc->tp, agi_bp, 0, BBTOB(agi_bp->b_length) - 1); | ||
860 | |||
861 | /* Now reinitialize the in-core counters if necessary. */ | ||
862 | pag = sc->sa.pag; | ||
863 | pag->pagi_count = be32_to_cpu(agi->agi_count); | ||
864 | pag->pagi_freecount = be32_to_cpu(agi->agi_freecount); | ||
865 | pag->pagi_init = 1; | ||
866 | |||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | /* Repair the AGI. */ | ||
871 | int | ||
872 | xrep_agi( | ||
873 | struct xfs_scrub *sc) | ||
874 | { | ||
875 | struct xrep_find_ag_btree fab[XREP_AGI_MAX] = { | ||
876 | [XREP_AGI_INOBT] = { | ||
877 | .rmap_owner = XFS_RMAP_OWN_INOBT, | ||
878 | .buf_ops = &xfs_inobt_buf_ops, | ||
879 | .magic = XFS_IBT_CRC_MAGIC, | ||
880 | }, | ||
881 | [XREP_AGI_FINOBT] = { | ||
882 | .rmap_owner = XFS_RMAP_OWN_INOBT, | ||
883 | .buf_ops = &xfs_inobt_buf_ops, | ||
884 | .magic = XFS_FIBT_CRC_MAGIC, | ||
885 | }, | ||
886 | [XREP_AGI_END] = { | ||
887 | .buf_ops = NULL | ||
888 | }, | ||
889 | }; | ||
890 | struct xfs_agi old_agi; | ||
891 | struct xfs_mount *mp = sc->mp; | ||
892 | struct xfs_buf *agi_bp; | ||
893 | struct xfs_agi *agi; | ||
894 | int error; | ||
895 | |||
896 | /* We require the rmapbt to rebuild anything. */ | ||
897 | if (!xfs_sb_version_hasrmapbt(&mp->m_sb)) | ||
898 | return -EOPNOTSUPP; | ||
899 | |||
900 | xchk_perag_get(sc->mp, &sc->sa); | ||
901 | /* | ||
902 | * Make sure we have the AGI buffer, as scrub might have decided it | ||
903 | * was corrupt after xfs_ialloc_read_agi failed with -EFSCORRUPTED. | ||
904 | */ | ||
905 | error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp, | ||
906 | XFS_AG_DADDR(mp, sc->sa.agno, XFS_AGI_DADDR(mp)), | ||
907 | XFS_FSS_TO_BB(mp, 1), 0, &agi_bp, NULL); | ||
908 | if (error) | ||
909 | return error; | ||
910 | agi_bp->b_ops = &xfs_agi_buf_ops; | ||
911 | agi = XFS_BUF_TO_AGI(agi_bp); | ||
912 | |||
913 | /* Find the AGI btree roots. */ | ||
914 | error = xrep_agi_find_btrees(sc, fab); | ||
915 | if (error) | ||
916 | return error; | ||
917 | |||
918 | /* Start rewriting the header and implant the btrees we found. */ | ||
919 | xrep_agi_init_header(sc, agi_bp, &old_agi); | ||
920 | xrep_agi_set_roots(sc, agi, fab); | ||
921 | error = xrep_agi_calc_from_btrees(sc, agi_bp); | ||
922 | if (error) | ||
923 | goto out_revert; | ||
924 | |||
925 | /* Reinitialize in-core state. */ | ||
926 | return xrep_agi_commit_new(sc, agi_bp); | ||
927 | |||
928 | out_revert: | ||
929 | /* Mark the incore AGI state stale and revert the AGI. */ | ||
930 | sc->sa.pag->pagi_init = 0; | ||
931 | memcpy(agi, &old_agi, sizeof(old_agi)); | ||
932 | return error; | ||
933 | } | ||
diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h index 1d283360b5ab..9de321eee4ab 100644 --- a/fs/xfs/scrub/repair.h +++ b/fs/xfs/scrub/repair.h | |||
@@ -60,6 +60,7 @@ int xrep_probe(struct xfs_scrub *sc); | |||
60 | int xrep_superblock(struct xfs_scrub *sc); | 60 | int xrep_superblock(struct xfs_scrub *sc); |
61 | int xrep_agf(struct xfs_scrub *sc); | 61 | int xrep_agf(struct xfs_scrub *sc); |
62 | int xrep_agfl(struct xfs_scrub *sc); | 62 | int xrep_agfl(struct xfs_scrub *sc); |
63 | int xrep_agi(struct xfs_scrub *sc); | ||
63 | 64 | ||
64 | #else | 65 | #else |
65 | 66 | ||
@@ -85,6 +86,7 @@ xrep_calc_ag_resblks( | |||
85 | #define xrep_superblock xrep_notsupported | 86 | #define xrep_superblock xrep_notsupported |
86 | #define xrep_agf xrep_notsupported | 87 | #define xrep_agf xrep_notsupported |
87 | #define xrep_agfl xrep_notsupported | 88 | #define xrep_agfl xrep_notsupported |
89 | #define xrep_agi xrep_notsupported | ||
88 | 90 | ||
89 | #endif /* CONFIG_XFS_ONLINE_REPAIR */ | 91 | #endif /* CONFIG_XFS_ONLINE_REPAIR */ |
90 | 92 | ||
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 2670f4cf62f4..4bfae1e61d30 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c | |||
@@ -226,7 +226,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { | |||
226 | .type = ST_PERAG, | 226 | .type = ST_PERAG, |
227 | .setup = xchk_setup_fs, | 227 | .setup = xchk_setup_fs, |
228 | .scrub = xchk_agi, | 228 | .scrub = xchk_agi, |
229 | .repair = xrep_notsupported, | 229 | .repair = xrep_agi, |
230 | }, | 230 | }, |
231 | [XFS_SCRUB_TYPE_BNOBT] = { /* bnobt */ | 231 | [XFS_SCRUB_TYPE_BNOBT] = { /* bnobt */ |
232 | .type = ST_PERAG, | 232 | .type = ST_PERAG, |