diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2020-10-25 17:15:48 -0700 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2020-10-26 18:32:24 -0700 |
commit | e8855fc16f125ed778dea1b0c9c7c3e9c92f8870 (patch) | |
tree | 894190c3df876815dbe4f9e9065d634896f678fa /fs/xfs/scrub/repair.c | |
parent | 32c6e10d243d14d2e20bd8471d761f12785e20f0 (diff) |
xfs: online repair of realtime summariesrepair-rtsummary_2020-10-26
Repair the realtime summary data by constructing a new rtsummary file in
the scrub temporary file, then atomically swapping the contents.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Diffstat (limited to 'fs/xfs/scrub/repair.c')
-rw-r--r-- | fs/xfs/scrub/repair.c | 177 |
1 files changed, 176 insertions, 1 deletions
diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 222ab8746513..8a5d2ea26fdf 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -39,6 +39,7 @@ #include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/bitmap.h" +#include "scrub/xfile.h" /* * Attempt to repair some metadata, if the metadata is corrupt and userspace @@ -165,9 +166,21 @@ int xrep_roll_trans( struct xfs_scrub *sc) { + int error; + if (!sc->ip) return xrep_roll_ag_trans(sc); - return xfs_trans_roll_inode(&sc->tp, sc->ip); + + /* + * Roll the transaction with the inode we're fixing and the temp inode, + * so that neither can pin the log. + */ + if (sc->tempip) + xfs_trans_log_inode(sc->tp, sc->tempip, XFS_ILOG_CORE); + error = xfs_trans_roll_inode(&sc->tp, sc->ip); + if (sc->tempip) + xfs_trans_ijoin(sc->tp, sc->tempip, 0); + return error; } /* @@ -1618,3 +1631,165 @@ out_release_inode: return error; } + +/* + * Make sure that the given range of the data fork of the temporary file is + * mapped to written blocks. The caller must ensure that both inodes are + * joined to the transaction. + */ +int +xrep_fallocate( + struct xfs_scrub *sc, + xfs_fileoff_t off, + xfs_filblks_t len) +{ + struct xfs_bmbt_irec map; + xfs_fileoff_t end = off + len; + int nmaps; + int error = 0; + + ASSERT(sc->tempip != NULL); + ASSERT(!XFS_NOT_DQATTACHED(sc->mp, sc->tempip)); + + while (off < len) { + /* + * If we have a real extent mapping this block then we're + * in ok shape. + */ + nmaps = 1; + error = xfs_bmapi_read(sc->tempip, off, end - off, &map, &nmaps, + XFS_DATA_FORK); + if (error) + break; + + if (nmaps == 1 && xfs_bmap_is_written_extent(&map)) { + off += map.br_startblock; + continue; + } + + /* + * If we find a delalloc reservation then something is very + * very wrong. Bail out. + */ + if (map.br_startblock == DELAYSTARTBLOCK) + return -EFSCORRUPTED; + + /* + * Make sure this rtsum block has a real zeroed extent + * allocated to it. + */ + nmaps = 1; + error = xfs_bmapi_write(sc->tp, sc->tempip, off, end - off, + XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO, 0, &map, + &nmaps); + if (error) + break; + + error = xrep_roll_trans(sc); + if (error) + break; + off += map.br_startblock; + } + + return error; +} + +/* + * Write a number of bytes from the xfile into the temp file. The copybuf must + * be large enough to hold one filesystem block's worth of data. The caller + * must join both inodes to the transaction. + */ +int +xrep_set_file_contents( + struct xfs_scrub *sc, + const struct xfs_buf_ops *ops, + enum xfs_blft type, + xfs_fileoff_t isize) +{ + LIST_HEAD(buffers_list); + struct xfs_bmbt_irec map; + struct xfs_mount *mp = sc->mp; + struct xfs_buf *bp; + xfs_rtblock_t off = 0; + loff_t pos = 0; + unsigned int nr_buffers = 0; + int nmaps; + int error = 0; + + ASSERT(S_ISREG(VFS_I(sc->tempip)->i_mode)); + + for (; pos < isize; pos += mp->m_sb.sb_blocksize, off++) { + size_t count; + + /* Read block mapping for this file block. */ + nmaps = 1; + error = xfs_bmapi_read(sc->tempip, off, 1, &map, &nmaps, 0); + if (error) + goto out; + if (nmaps == 0 || !xfs_bmap_is_written_extent(&map)) { + error = -EFSCORRUPTED; + goto out; + } + + /* Get the metadata buffer for this offset in the file. */ + error = xfs_trans_get_buf(sc->tp, mp->m_ddev_targp, + XFS_FSB_TO_DADDR(mp, map.br_startblock), + mp->m_bsize, 0, &bp); + if (error) + goto out; + bp->b_ops = ops; + xfs_trans_buf_set_type(sc->tp, bp, type); + + /* Read in a block's worth of data from the xfile. */ + count = min_t(loff_t, isize - pos, mp->m_sb.sb_blocksize); + error = xfile_pread(sc->xfile, bp->b_addr, count, pos); + if (error) { + xfs_trans_brelse(sc->tp, bp); + goto out; + } + + /* + * Put this buffer on the delwri list so we can write them all + * out in batches. + */ + xfs_buf_delwri_queue(bp, &buffers_list); + xfs_trans_brelse(sc->tp, bp); + nr_buffers++; + + /* + * If we have more than 256K of data to write out, flush it to + * disk so we don't use up too much memory. + */ + if (XFS_FSB_TO_B(mp, nr_buffers) > 262144) { + error = xfs_buf_delwri_submit(&buffers_list); + if (error) + goto out; + nr_buffers = 0; + } + } + + /* + * Write the new blocks to disk. If the ordered list isn't empty after + * that, then something went wrong and we have to fail. This should + * never happen, but we'll check anyway. + */ + error = xfs_buf_delwri_submit(&buffers_list); + if (error) + goto out; + if (!list_empty(&buffers_list)) { + ASSERT(list_empty(&buffers_list)); + return -EIO; + } + + /* Set the new inode size, if needed. */ + if (sc->tempip->i_d.di_size != isize) { + sc->tempip->i_d.di_size = isize; + xfs_trans_log_inode(sc->tp, sc->tempip, XFS_ILOG_CORE); + return xrep_roll_trans(sc); + } + + return 0; +out: + xfs_buf_delwri_cancel(&buffers_list); + return error; +} |