blob: 781d8968446c27e21ae102e8d9fdc48bbbad43f5 [file] [log] [blame]
// 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_rgno(rtg)];
struct xfs_mount *mp = rtg_mount(rtg);
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_rgno(rtg),
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 = NULL;
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"));
while ((rtg = xfs_rtgroup_next(mp, 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_mount(rtg);
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_rgno(rtg), 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_rgno(rtg), 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_mount(rtg);
const char *filename = libxfs_rtginode_name(type);
struct xfs_inode *ip = rtg->rtg_inodes[type];
xfs_fileoff_t bno = 0;
int error;
ASSERT(!xfs_has_zoned(mp));
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;
tp = libxfs_trans_alloc_empty(mp);
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 = NULL;
if (need_rbmino)
return;
if (!xfs_has_rtgroups(mp))
try_load_sb_rtfile(mp, XFS_RTGI_BITMAP);
while ((rtg = xfs_rtgroup_next(mp, rtg))) {
check_rtfile_contents(rtg, XFS_RTGI_BITMAP,
rt_computed[rtg_rgno(rtg)].bmp,
mp->m_sb.sb_rbmblocks);
}
}
void
check_rtsummary(
struct xfs_mount *mp)
{
struct xfs_rtgroup *rtg = NULL;
if (need_rsumino)
return;
if (!xfs_has_rtgroups(mp))
try_load_sb_rtfile(mp, XFS_RTGI_SUMMARY);
while ((rtg = xfs_rtgroup_next(mp, rtg))) {
check_rtfile_contents(rtg, XFS_RTGI_SUMMARY,
rt_computed[rtg_rgno(rtg)].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_mount(rtg)->m_sb.sb_rbmblocks,
rt_computed[rtg_rgno(rtg)].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_mount(rtg)->m_rsumblocks,
rt_computed[rtg_rgno(rtg)].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 = NULL;
while ((rtg = xfs_rtgroup_next(mp, 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_mount(rtg))) {
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 = NULL;
struct xfs_trans *tp;
int error, err2;
int i;
tp = libxfs_trans_alloc_empty(mp);
if (xfs_has_rtgroups(mp) && mp->m_sb.sb_rgcount > 0) {
error = -libxfs_rtginode_load_parent(tp);
if (error)
goto out_cancel;
}
while ((rtg = xfs_rtgroup_next(mp, 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);
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 = NULL;
unsigned int i;
while ((rtg = xfs_rtgroup_next(mp, 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), &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);
}