| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "libxfs.h" |
| #include "avl.h" |
| #include "globals.h" |
| #include "agheader.h" |
| #include "incore.h" |
| #include "dinode.h" |
| #include "protos.h" |
| #include "err_protos.h" |
| #include "libfrog/bitmap.h" |
| #include "rt.h" |
| |
| /* Bitmap of rt group inodes */ |
| static struct bitmap *rtg_inodes[XFS_RTGI_MAX]; |
| static bool rtginodes_bad[XFS_RTGI_MAX]; |
| |
| /* Computed rt bitmap/summary data */ |
| struct rtg_computed { |
| union xfs_rtword_raw *bmp; |
| union xfs_suminfo_raw *sum; |
| }; |
| struct rtg_computed *rt_computed; |
| |
| static inline void |
| set_rtword( |
| struct xfs_mount *mp, |
| union xfs_rtword_raw *word, |
| xfs_rtword_t value) |
| { |
| if (xfs_has_rtgroups(mp)) |
| word->rtg = cpu_to_be32(value); |
| else |
| word->old = value; |
| } |
| |
| static inline void |
| inc_sumcount( |
| struct xfs_mount *mp, |
| union xfs_suminfo_raw *info, |
| xfs_rtsumoff_t index) |
| { |
| union xfs_suminfo_raw *p = info + index; |
| |
| if (xfs_has_rtgroups(mp)) |
| be32_add_cpu(&p->rtg, 1); |
| else |
| p->old++; |
| } |
| |
| static void |
| generate_rtgroup_rtinfo( |
| struct xfs_rtgroup *rtg) |
| { |
| struct rtg_computed *comp = &rt_computed[rtg->rtg_rgno]; |
| struct xfs_mount *mp = rtg->rtg_mount; |
| unsigned int bitsperblock = |
| mp->m_blockwsize << XFS_NBWORDLOG; |
| xfs_rtxnum_t extno = 0; |
| xfs_rtxnum_t start_ext = 0; |
| int bmbno = 0; |
| int start_bmbno = 0; |
| bool in_extent = false; |
| unsigned long long wordcnt; |
| union xfs_rtword_raw *words; |
| |
| wordcnt = XFS_FSB_TO_B(mp, mp->m_sb.sb_rbmblocks) >> XFS_WORDLOG; |
| comp->bmp = calloc(wordcnt, sizeof(union xfs_rtword_raw)); |
| if (!comp->bmp) |
| do_error( |
| _("couldn't allocate memory for incore realtime bitmap.\n")); |
| words = comp->bmp; |
| |
| wordcnt = XFS_FSB_TO_B(mp, mp->m_rsumblocks) >> XFS_WORDLOG; |
| comp->sum = calloc(wordcnt, sizeof(union xfs_suminfo_raw)); |
| if (!comp->sum) |
| do_error( |
| _("couldn't allocate memory for incore realtime summary info.\n")); |
| |
| /* |
| * Slower but simple, don't play around with trying to set things one |
| * word at a time, just set bit as required. Have to track start and |
| * end (size) of each range of free extents to set the summary info |
| * properly. |
| */ |
| while (extno < rtg->rtg_extents) { |
| xfs_rtword_t freebit = 1; |
| xfs_rtword_t bits = 0; |
| int state, i; |
| |
| set_rtword(mp, words, 0); |
| for (i = 0; i < sizeof(xfs_rtword_t) * NBBY; i++) { |
| if (extno == rtg->rtg_extents) |
| break; |
| |
| /* |
| * Note: for the RTG case it might make sense to use |
| * get_rgbmap_ext here and generate multiple bitmap |
| * entries per lookup. |
| */ |
| if (xfs_has_rtgroups(mp)) |
| state = get_rgbmap(rtg->rtg_rgno, |
| extno * mp->m_sb.sb_rextsize); |
| else |
| state = get_rtbmap(extno); |
| if (state == XR_E_FREE) { |
| sb_frextents++; |
| bits |= freebit; |
| |
| if (!in_extent) { |
| start_ext = extno; |
| start_bmbno = bmbno; |
| in_extent = true; |
| } |
| } else if (in_extent) { |
| uint64_t len = extno - start_ext; |
| xfs_rtsumoff_t offs; |
| |
| offs = xfs_rtsumoffs(mp, libxfs_highbit64(len), |
| start_bmbno); |
| inc_sumcount(mp, comp->sum, offs); |
| in_extent = false; |
| } |
| |
| freebit <<= 1; |
| extno++; |
| } |
| set_rtword(mp, words, bits); |
| words++; |
| |
| if (extno % bitsperblock == 0) |
| bmbno++; |
| } |
| |
| if (in_extent) { |
| uint64_t len = extno - start_ext; |
| xfs_rtsumoff_t offs; |
| |
| offs = xfs_rtsumoffs(mp, libxfs_highbit64(len), start_bmbno); |
| inc_sumcount(mp, comp->sum, offs); |
| } |
| } |
| |
| /* |
| * generate the real-time bitmap and summary info based on the |
| * incore realtime extent map. |
| */ |
| void |
| generate_rtinfo( |
| struct xfs_mount *mp) |
| { |
| struct xfs_rtgroup *rtg; |
| xfs_rgnumber_t rgno; |
| |
| rt_computed = calloc(mp->m_sb.sb_rgcount, sizeof(struct rtg_computed)); |
| if (!rt_computed) |
| do_error( |
| _("couldn't allocate memory for incore realtime info.\n")); |
| |
| for_each_rtgroup(mp, rgno, rtg) |
| generate_rtgroup_rtinfo(rtg); |
| |
| if (mp->m_sb.sb_frextents != sb_frextents) { |
| do_warn(_("sb_frextents %" PRIu64 ", counted %" PRIu64 "\n"), |
| mp->m_sb.sb_frextents, sb_frextents); |
| } |
| } |
| |
| static void |
| check_rtwords( |
| struct xfs_rtgroup *rtg, |
| const char *filename, |
| unsigned long long bno, |
| void *ondisk, |
| void *incore) |
| { |
| struct xfs_mount *mp = rtg->rtg_mount; |
| unsigned int wordcnt = mp->m_blockwsize; |
| union xfs_rtword_raw *o = ondisk, *i = incore; |
| int badstart = -1; |
| unsigned int j; |
| |
| if (memcmp(ondisk, incore, wordcnt << XFS_WORDLOG) == 0) |
| return; |
| |
| for (j = 0; j < wordcnt; j++, o++, i++) { |
| if (o->old == i->old) { |
| /* Report a range of inconsistency that just ended. */ |
| if (badstart >= 0) |
| do_warn( |
| _("discrepancy in %s (%u) at dblock 0x%llx words 0x%x-0x%x/0x%x\n"), |
| filename, rtg->rtg_rgno, bno, |
| badstart, j - 1, wordcnt); |
| badstart = -1; |
| continue; |
| } |
| |
| if (badstart == -1) |
| badstart = j; |
| } |
| |
| if (badstart >= 0) |
| do_warn( |
| _("discrepancy in %s (%u) at dblock 0x%llx words 0x%x-0x%x/0x%x\n"), |
| filename, rtg->rtg_rgno, bno, |
| badstart, wordcnt, wordcnt); |
| } |
| |
| static void |
| check_rtfile_contents( |
| struct xfs_rtgroup *rtg, |
| enum xfs_rtg_inodes type, |
| void *buf, |
| xfs_fileoff_t filelen) |
| { |
| struct xfs_mount *mp = rtg->rtg_mount; |
| const char *filename = libxfs_rtginode_name(type); |
| struct xfs_inode *ip = rtg->rtg_inodes[type]; |
| xfs_fileoff_t bno = 0; |
| int error; |
| |
| if (!ip) { |
| do_warn(_("unable to open %s file\n"), filename); |
| return; |
| } |
| |
| if (ip->i_disk_size != XFS_FSB_TO_B(mp, filelen)) { |
| do_warn(_("expected %s file size %llu, found %llu\n"), |
| filename, |
| (unsigned long long)XFS_FSB_TO_B(mp, filelen), |
| (unsigned long long)ip->i_disk_size); |
| } |
| |
| while (bno < filelen) { |
| struct xfs_bmbt_irec map; |
| struct xfs_buf *bp; |
| unsigned int offset = 0; |
| int nmap = 1; |
| |
| error = -libxfs_bmapi_read(ip, bno, 1, &map, &nmap, 0); |
| if (error) { |
| do_warn(_("unable to read %s mapping, err %d\n"), |
| filename, error); |
| break; |
| } |
| |
| if (map.br_startblock == HOLESTARTBLOCK) { |
| do_warn(_("hole in %s file at dblock 0x%llx\n"), |
| filename, (unsigned long long)bno); |
| break; |
| } |
| |
| error = -libxfs_buf_read_uncached(mp->m_dev, |
| XFS_FSB_TO_DADDR(mp, map.br_startblock), |
| XFS_FSB_TO_BB(mp, 1), 0, &bp, |
| xfs_rtblock_ops(mp, type)); |
| if (error) { |
| do_warn(_("unable to read %s at dblock 0x%llx, err %d\n"), |
| filename, (unsigned long long)bno, error); |
| break; |
| } |
| |
| if (xfs_has_rtgroups(mp)) { |
| struct xfs_rtbuf_blkinfo *hdr = bp->b_addr; |
| |
| if (hdr->rt_owner != cpu_to_be64(ip->i_ino)) { |
| do_warn( |
| _("corrupt owner in %s at dblock 0x%llx\n"), |
| filename, (unsigned long long)bno); |
| } |
| |
| offset = sizeof(*hdr); |
| } |
| |
| check_rtwords(rtg, filename, bno, bp->b_addr + offset, buf); |
| |
| buf += mp->m_blockwsize << XFS_WORDLOG; |
| bno++; |
| libxfs_buf_relse(bp); |
| } |
| } |
| |
| /* |
| * Try to load a sb-rooted rt metadata file now, since earlier phases may have |
| * fixed verifier problems in the root inode chunk. |
| */ |
| static void |
| try_load_sb_rtfile( |
| struct xfs_mount *mp, |
| enum xfs_rtg_inodes type) |
| { |
| struct xfs_rtgroup *rtg = libxfs_rtgroup_grab(mp, 0); |
| struct xfs_trans *tp; |
| int error; |
| |
| if (rtg->rtg_inodes[type]) |
| goto out_rtg; |
| |
| error = -libxfs_trans_alloc_empty(mp, &tp); |
| if (error) |
| goto out_rtg; |
| |
| |
| error = -libxfs_rtginode_load(rtg, type, tp); |
| if (error) |
| goto out_cancel; |
| |
| /* If we can't load the inode, signal to phase 6 to recreate it. */ |
| if (!rtg->rtg_inodes[type]) { |
| switch (type) { |
| case XFS_RTGI_BITMAP: |
| need_rbmino = 1; |
| break; |
| case XFS_RTGI_SUMMARY: |
| need_rsumino = 1; |
| break; |
| default: |
| ASSERT(0); |
| break; |
| } |
| } |
| |
| out_cancel: |
| libxfs_trans_cancel(tp); |
| out_rtg: |
| libxfs_rtgroup_rele(rtg); |
| } |
| |
| void |
| check_rtbitmap( |
| struct xfs_mount *mp) |
| { |
| struct xfs_rtgroup *rtg; |
| xfs_rgnumber_t rgno; |
| |
| if (need_rbmino) |
| return; |
| |
| if (!xfs_has_rtgroups(mp)) |
| try_load_sb_rtfile(mp, XFS_RTGI_BITMAP); |
| |
| for_each_rtgroup(mp, rgno, rtg) { |
| check_rtfile_contents(rtg, XFS_RTGI_BITMAP, |
| rt_computed[rtg->rtg_rgno].bmp, |
| mp->m_sb.sb_rbmblocks); |
| } |
| } |
| |
| void |
| check_rtsummary( |
| struct xfs_mount *mp) |
| { |
| struct xfs_rtgroup *rtg; |
| xfs_rgnumber_t rgno; |
| |
| if (need_rsumino) |
| return; |
| |
| if (!xfs_has_rtgroups(mp)) |
| try_load_sb_rtfile(mp, XFS_RTGI_SUMMARY); |
| |
| for_each_rtgroup(mp, rgno, rtg) { |
| check_rtfile_contents(rtg, XFS_RTGI_SUMMARY, |
| rt_computed[rtg->rtg_rgno].sum, |
| mp->m_rsumblocks); |
| } |
| } |
| |
| void |
| fill_rtbitmap( |
| struct xfs_rtgroup *rtg) |
| { |
| int error; |
| |
| /* |
| * For file systems without a RT subvolume we have the bitmap and |
| * summary files, but they are empty. In that case rt_computed is |
| * NULL. |
| */ |
| if (!rt_computed) |
| return; |
| |
| error = -libxfs_rtfile_initialize_blocks(rtg, XFS_RTGI_BITMAP, |
| 0, rtg->rtg_mount->m_sb.sb_rbmblocks, |
| rt_computed[rtg->rtg_rgno].bmp); |
| if (error) |
| do_error( |
| _("couldn't re-initialize realtime bitmap inode, error %d\n"), error); |
| } |
| |
| void |
| fill_rtsummary( |
| struct xfs_rtgroup *rtg) |
| { |
| int error; |
| |
| /* |
| * For file systems without a RT subvolume we have the bitmap and |
| * summary files, but they are empty. In that case rt_computed is |
| * NULL. |
| */ |
| if (!rt_computed) |
| return; |
| |
| error = -libxfs_rtfile_initialize_blocks(rtg, XFS_RTGI_SUMMARY, |
| 0, rtg->rtg_mount->m_rsumblocks, |
| rt_computed[rtg->rtg_rgno].sum); |
| if (error) |
| do_error( |
| _("couldn't re-initialize realtime summary inode, error %d\n"), error); |
| } |
| |
| bool |
| is_rtgroup_inode( |
| xfs_ino_t ino, |
| enum xfs_rtg_inodes type) |
| { |
| if (!rtg_inodes[type]) |
| return false; |
| return bitmap_test(rtg_inodes[type], ino, 1); |
| } |
| |
| bool |
| rtgroup_inodes_were_bad( |
| enum xfs_rtg_inodes type) |
| { |
| return rtginodes_bad[type]; |
| } |
| |
| void |
| mark_rtgroup_inodes_bad( |
| struct xfs_mount *mp, |
| enum xfs_rtg_inodes type) |
| { |
| struct xfs_rtgroup *rtg; |
| xfs_rgnumber_t rgno; |
| |
| for_each_rtgroup(mp, rgno, rtg) |
| libxfs_rtginode_irele(&rtg->rtg_inodes[type]); |
| |
| rtginodes_bad[type] = true; |
| } |
| |
| static inline int |
| mark_rtginode( |
| struct xfs_trans *tp, |
| struct xfs_rtgroup *rtg, |
| enum xfs_rtg_inodes type) |
| { |
| struct xfs_inode *ip; |
| int error; |
| |
| if (!xfs_rtginode_enabled(rtg, type)) |
| return 0; |
| |
| error = -libxfs_rtginode_load(rtg, type, tp); |
| if (error) |
| goto out_corrupt; |
| |
| ip = rtg->rtg_inodes[type]; |
| if (!ip) |
| goto out_corrupt; |
| |
| if (xfs_has_rtgroups(rtg->rtg_mount)) { |
| if (bitmap_test(rtg_inodes[type], ip->i_ino, 1)) { |
| error = EFSCORRUPTED; |
| goto out_corrupt; |
| } |
| |
| error = bitmap_set(rtg_inodes[type], ip->i_ino, 1); |
| if (error) |
| goto out_corrupt; |
| } |
| |
| /* |
| * Phase 3 will clear the ondisk inodes of all rt metadata files, but |
| * it doesn't reset any blocks. Keep the incore inodes loaded so that |
| * phase 4 can check the rt metadata. These inodes must be dropped |
| * before rebuilding can begin during phase 6. |
| */ |
| return 0; |
| |
| out_corrupt: |
| rtginodes_bad[type] = true; |
| return error; |
| } |
| |
| /* Mark the reachable rt metadata inodes prior to the inode scan. */ |
| void |
| discover_rtgroup_inodes( |
| struct xfs_mount *mp) |
| { |
| struct xfs_rtgroup *rtg; |
| struct xfs_trans *tp; |
| xfs_rgnumber_t rgno; |
| int error, err2; |
| int i; |
| |
| error = -libxfs_trans_alloc_empty(mp, &tp); |
| if (error) |
| goto out; |
| if (xfs_has_rtgroups(mp) && mp->m_sb.sb_rgcount > 0) { |
| error = -libxfs_rtginode_load_parent(tp); |
| if (error) |
| goto out_cancel; |
| } |
| |
| for_each_rtgroup(mp, rgno, rtg) { |
| for (i = 0; i < XFS_RTGI_MAX; i++) { |
| err2 = mark_rtginode(tp, rtg, i); |
| if (err2 && !error) |
| error = err2; |
| } |
| } |
| |
| out_cancel: |
| libxfs_trans_cancel(tp); |
| out: |
| if (xfs_has_rtgroups(mp) && error) { |
| /* |
| * Old xfs_repair didn't complain if rtbitmaps didn't load |
| * until phase 5, so only turn on extra warnings during phase 2 |
| * for newer filesystems. |
| */ |
| switch (error) { |
| case EFSCORRUPTED: |
| do_warn( |
| _("corruption in metadata directory tree while discovering rt group inodes\n")); |
| break; |
| default: |
| do_warn( |
| _("couldn't discover rt group inodes, err %d\n"), |
| error); |
| break; |
| } |
| } |
| } |
| |
| /* Unload incore rtgroup inodes before rebuilding rt metadata. */ |
| void |
| unload_rtgroup_inodes( |
| struct xfs_mount *mp) |
| { |
| struct xfs_rtgroup *rtg; |
| xfs_rgnumber_t rgno; |
| unsigned int i; |
| |
| for_each_rtgroup(mp, rgno, rtg) |
| for (i = 0; i < XFS_RTGI_MAX; i++) |
| libxfs_rtginode_irele(&rtg->rtg_inodes[i]); |
| |
| libxfs_rtginode_irele(&mp->m_rtdirip); |
| } |
| |
| void |
| init_rtgroup_inodes(void) |
| { |
| unsigned int i; |
| int error; |
| |
| for (i = 0; i < XFS_RTGI_MAX; i++) { |
| error = bitmap_alloc(&rtg_inodes[i]); |
| if (error) |
| break; |
| } |
| |
| if (error) |
| do_error(_("could not allocate rtginode bitmap, err=%d!\n"), |
| error); |
| } |
| |
| void |
| free_rtgroup_inodes(void) |
| { |
| int i; |
| |
| for (i = 0; i < XFS_RTGI_MAX; i++) |
| bitmap_free(&rtg_inodes[i]); |
| } |
| |
| void |
| check_rtsb( |
| struct xfs_mount *mp) |
| { |
| struct xfs_buf *bp; |
| int error; |
| |
| error = -libxfs_buf_read_uncached(mp->m_rtdev_targp, XFS_RTSB_DADDR, |
| XFS_FSB_TO_BB(mp, 1), 0, &bp, &xfs_rtsb_buf_ops); |
| if (!error) { |
| libxfs_buf_relse(bp); |
| return; |
| } |
| |
| if (no_modify) { |
| do_warn(_("would rewrite realtime superblock\n")); |
| return; |
| } |
| |
| /* |
| * Rewrite the rt superblock so that an update to the primary fs |
| * superblock will not get confused by the non-matching rtsb. |
| */ |
| do_warn(_("will rewrite realtime superblock\n")); |
| rewrite_rtsb(mp); |
| } |
| |
| void |
| rewrite_rtsb( |
| struct xfs_mount *mp) |
| { |
| struct xfs_buf *rtsb_bp; |
| struct xfs_buf *sb_bp = libxfs_getsb(mp); |
| int error; |
| |
| if (!sb_bp) |
| do_error( |
| _("couldn't grab primary sb to update realtime sb\n")); |
| |
| error = -libxfs_buf_get_uncached(mp->m_rtdev_targp, |
| XFS_FSB_TO_BB(mp, 1), XFS_RTSB_DADDR, &rtsb_bp); |
| if (error) |
| do_error( |
| _("couldn't grab realtime superblock\n")); |
| |
| rtsb_bp->b_maps[0].bm_bn = XFS_RTSB_DADDR; |
| rtsb_bp->b_ops = &xfs_rtsb_buf_ops; |
| |
| libxfs_update_rtsb(rtsb_bp, sb_bp); |
| libxfs_buf_mark_dirty(rtsb_bp); |
| libxfs_buf_relse(rtsb_bp); |
| libxfs_buf_relse(sb_bp); |
| } |