2018-05-14 06:10:08 +00:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
|
|
|
* Copyright (c) 2018 Red Hat, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "xfs.h"
|
|
|
|
#include "xfs_fs.h"
|
|
|
|
#include "xfs_shared.h"
|
|
|
|
#include "xfs_format.h"
|
|
|
|
#include "xfs_trans_resv.h"
|
2019-06-29 02:30:21 +00:00
|
|
|
#include "xfs_bit.h"
|
2018-05-14 06:10:08 +00:00
|
|
|
#include "xfs_sb.h"
|
|
|
|
#include "xfs_mount.h"
|
|
|
|
#include "xfs_btree.h"
|
|
|
|
#include "xfs_alloc_btree.h"
|
|
|
|
#include "xfs_rmap_btree.h"
|
|
|
|
#include "xfs_alloc.h"
|
2018-05-14 06:10:08 +00:00
|
|
|
#include "xfs_ialloc.h"
|
2018-05-14 06:10:08 +00:00
|
|
|
#include "xfs_rmap.h"
|
|
|
|
#include "xfs_ag.h"
|
2019-04-12 14:41:17 +00:00
|
|
|
#include "xfs_ag_resv.h"
|
2019-04-12 14:41:18 +00:00
|
|
|
#include "xfs_health.h"
|
2021-03-24 02:05:38 +00:00
|
|
|
#include "xfs_error.h"
|
|
|
|
#include "xfs_bmap.h"
|
|
|
|
#include "xfs_defer.h"
|
|
|
|
#include "xfs_log_format.h"
|
|
|
|
#include "xfs_trans.h"
|
2021-06-02 00:48:24 +00:00
|
|
|
#include "xfs_trace.h"
|
2021-06-02 00:48:24 +00:00
|
|
|
#include "xfs_inode.h"
|
|
|
|
#include "xfs_icache.h"
|
2024-11-04 04:18:38 +00:00
|
|
|
#include "xfs_group.h"
|
2023-02-12 22:14:42 +00:00
|
|
|
|
2021-06-02 00:48:24 +00:00
|
|
|
/*
|
|
|
|
* xfs_initialize_perag_data
|
|
|
|
*
|
|
|
|
* Read in each per-ag structure so we can count up the number of
|
|
|
|
* allocated inodes, free inodes and used filesystem blocks as this
|
|
|
|
* information is no longer persistent in the superblock. Once we have
|
|
|
|
* this information, write it into the in-core superblock structure.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_initialize_perag_data(
|
2021-06-02 00:48:51 +00:00
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t agcount)
|
2021-06-02 00:48:24 +00:00
|
|
|
{
|
2021-06-02 00:48:51 +00:00
|
|
|
xfs_agnumber_t index;
|
|
|
|
struct xfs_perag *pag;
|
|
|
|
struct xfs_sb *sbp = &mp->m_sb;
|
|
|
|
uint64_t ifree = 0;
|
|
|
|
uint64_t ialloc = 0;
|
|
|
|
uint64_t bfree = 0;
|
|
|
|
uint64_t bfreelst = 0;
|
|
|
|
uint64_t btree = 0;
|
|
|
|
uint64_t fdblocks;
|
|
|
|
int error = 0;
|
2021-06-02 00:48:24 +00:00
|
|
|
|
|
|
|
for (index = 0; index < agcount; index++) {
|
|
|
|
/*
|
2022-07-07 09:07:40 +00:00
|
|
|
* Read the AGF and AGI buffers to populate the per-ag
|
|
|
|
* structures for us.
|
2021-06-02 00:48:24 +00:00
|
|
|
*/
|
2022-07-07 09:07:24 +00:00
|
|
|
pag = xfs_perag_get(mp, index);
|
2022-07-07 09:07:40 +00:00
|
|
|
error = xfs_alloc_read_agf(pag, NULL, 0, NULL);
|
|
|
|
if (!error)
|
2024-04-15 21:54:03 +00:00
|
|
|
error = xfs_ialloc_read_agi(pag, NULL, 0, NULL);
|
2022-07-07 09:07:24 +00:00
|
|
|
if (error) {
|
|
|
|
xfs_perag_put(pag);
|
2021-06-02 00:48:24 +00:00
|
|
|
return error;
|
2022-07-07 09:07:24 +00:00
|
|
|
}
|
2022-07-07 09:07:16 +00:00
|
|
|
|
2021-06-02 00:48:24 +00:00
|
|
|
ifree += pag->pagi_freecount;
|
|
|
|
ialloc += pag->pagi_count;
|
|
|
|
bfree += pag->pagf_freeblks;
|
|
|
|
bfreelst += pag->pagf_flcount;
|
|
|
|
btree += pag->pagf_btreeblks;
|
|
|
|
xfs_perag_put(pag);
|
|
|
|
}
|
|
|
|
fdblocks = bfree + bfreelst + btree;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the new summary counts are obviously incorrect, fail the
|
|
|
|
* mount operation because that implies the AGFs are also corrupt.
|
|
|
|
* Clear FS_COUNTERS so that we don't unmount with a dirty log, which
|
|
|
|
* will prevent xfs_repair from fixing anything.
|
|
|
|
*/
|
|
|
|
if (fdblocks > sbp->sb_dblocks || ifree > ialloc) {
|
|
|
|
xfs_alert(mp, "AGF corruption. Please run xfs_repair.");
|
2024-02-22 20:31:02 +00:00
|
|
|
xfs_fs_mark_sick(mp, XFS_SICK_FS_COUNTERS);
|
2021-06-02 00:48:24 +00:00
|
|
|
error = -EFSCORRUPTED;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Overwrite incore superblock counters with just-read data */
|
|
|
|
spin_lock(&mp->m_sb_lock);
|
|
|
|
sbp->sb_ifree = ifree;
|
|
|
|
sbp->sb_icount = ialloc;
|
|
|
|
sbp->sb_fdblocks = fdblocks;
|
|
|
|
spin_unlock(&mp->m_sb_lock);
|
|
|
|
|
|
|
|
xfs_reinit_percpu_counters(mp);
|
|
|
|
out:
|
|
|
|
xfs_fs_mark_healthy(mp, XFS_SICK_FS_COUNTERS);
|
|
|
|
return error;
|
|
|
|
}
|
2018-05-14 06:10:08 +00:00
|
|
|
|
2024-11-04 04:18:38 +00:00
|
|
|
static void
|
|
|
|
xfs_perag_uninit(
|
|
|
|
struct xfs_group *xg)
|
|
|
|
{
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
struct xfs_perag *pag = to_perag(xg);
|
|
|
|
|
|
|
|
cancel_delayed_work_sync(&pag->pag_blockgc_work);
|
|
|
|
xfs_buf_cache_destroy(&pag->pag_bcache);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-06-02 00:48:24 +00:00
|
|
|
/*
|
2024-10-14 06:04:51 +00:00
|
|
|
* Free up the per-ag resources within the specified AG range.
|
2021-06-02 00:48:24 +00:00
|
|
|
*/
|
|
|
|
void
|
2024-10-14 06:04:51 +00:00
|
|
|
xfs_free_perag_range(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t first_agno,
|
|
|
|
xfs_agnumber_t end_agno)
|
|
|
|
|
2021-06-02 00:48:24 +00:00
|
|
|
{
|
|
|
|
xfs_agnumber_t agno;
|
|
|
|
|
2024-11-04 04:18:38 +00:00
|
|
|
for (agno = first_agno; agno < end_agno; agno++)
|
|
|
|
xfs_group_free(mp, agno, XG_TYPE_AG, xfs_perag_uninit);
|
2021-06-02 00:48:24 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 09:13:02 +00:00
|
|
|
/* Find the size of the AG, in blocks. */
|
|
|
|
static xfs_agblock_t
|
|
|
|
__xfs_ag_block_count(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t agno,
|
|
|
|
xfs_agnumber_t agcount,
|
|
|
|
xfs_rfsblock_t dblocks)
|
|
|
|
{
|
|
|
|
ASSERT(agno < agcount);
|
|
|
|
|
|
|
|
if (agno < agcount - 1)
|
|
|
|
return mp->m_sb.sb_agblocks;
|
|
|
|
return dblocks - (agno * mp->m_sb.sb_agblocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
xfs_agblock_t
|
|
|
|
xfs_ag_block_count(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t agno)
|
|
|
|
{
|
|
|
|
return __xfs_ag_block_count(mp, agno, mp->m_sb.sb_agcount,
|
|
|
|
mp->m_sb.sb_dblocks);
|
|
|
|
}
|
|
|
|
|
2022-07-07 09:13:10 +00:00
|
|
|
/* Calculate the first and last possible inode number in an AG. */
|
|
|
|
static void
|
|
|
|
__xfs_agino_range(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agblock_t eoag,
|
|
|
|
xfs_agino_t *first,
|
|
|
|
xfs_agino_t *last)
|
|
|
|
{
|
|
|
|
xfs_agblock_t bno;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the first inode, which will be in the first
|
|
|
|
* cluster-aligned block after the AGFL.
|
|
|
|
*/
|
|
|
|
bno = round_up(XFS_AGFL_BLOCK(mp) + 1, M_IGEO(mp)->cluster_align);
|
|
|
|
*first = XFS_AGB_TO_AGINO(mp, bno);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the last inode, which will be at the end of the
|
|
|
|
* last (aligned) cluster that can be allocated in the AG.
|
|
|
|
*/
|
|
|
|
bno = round_down(eoag, M_IGEO(mp)->cluster_align);
|
|
|
|
*last = XFS_AGB_TO_AGINO(mp, bno) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
xfs_agino_range(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t agno,
|
|
|
|
xfs_agino_t *first,
|
|
|
|
xfs_agino_t *last)
|
|
|
|
{
|
|
|
|
return __xfs_agino_range(mp, xfs_ag_block_count(mp, agno), first, last);
|
|
|
|
}
|
|
|
|
|
2024-11-04 04:18:36 +00:00
|
|
|
/*
|
|
|
|
* Update the perag of the previous tail AG if it has been changed during
|
|
|
|
* recovery (i.e. recovery of a growfs).
|
|
|
|
*/
|
2024-10-14 06:04:55 +00:00
|
|
|
int
|
|
|
|
xfs_update_last_ag_size(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t prev_agcount)
|
|
|
|
{
|
|
|
|
struct xfs_perag *pag = xfs_perag_grab(mp, prev_agcount - 1);
|
|
|
|
|
|
|
|
if (!pag)
|
|
|
|
return -EFSCORRUPTED;
|
|
|
|
pag->block_count = __xfs_ag_block_count(mp, prev_agcount - 1,
|
|
|
|
mp->m_sb.sb_agcount, mp->m_sb.sb_dblocks);
|
|
|
|
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
|
|
|
&pag->agino_max);
|
|
|
|
xfs_perag_rele(pag);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-11-04 04:18:36 +00:00
|
|
|
static int
|
|
|
|
xfs_perag_alloc(
|
2021-06-02 00:48:24 +00:00
|
|
|
struct xfs_mount *mp,
|
2024-11-04 04:18:36 +00:00
|
|
|
xfs_agnumber_t index,
|
|
|
|
xfs_agnumber_t agcount,
|
|
|
|
xfs_rfsblock_t dblocks)
|
2021-06-02 00:48:24 +00:00
|
|
|
{
|
|
|
|
struct xfs_perag *pag;
|
|
|
|
int error;
|
|
|
|
|
2024-11-04 04:18:36 +00:00
|
|
|
pag = kzalloc(sizeof(*pag), GFP_KERNEL);
|
|
|
|
if (!pag)
|
|
|
|
return -ENOMEM;
|
2021-06-02 00:48:24 +00:00
|
|
|
|
2021-11-10 02:18:50 +00:00
|
|
|
#ifdef __KERNEL__
|
2024-11-04 04:18:36 +00:00
|
|
|
/* Place kernel structure only init below this point. */
|
|
|
|
spin_lock_init(&pag->pag_ici_lock);
|
|
|
|
INIT_DELAYED_WORK(&pag->pag_blockgc_work, xfs_blockgc_worker);
|
|
|
|
INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC);
|
2021-11-10 02:18:50 +00:00
|
|
|
#endif /* __KERNEL__ */
|
2021-06-02 00:48:24 +00:00
|
|
|
|
2024-11-04 04:18:36 +00:00
|
|
|
error = xfs_buf_cache_init(&pag->pag_bcache);
|
|
|
|
if (error)
|
2024-11-04 04:18:41 +00:00
|
|
|
goto out_free_perag;
|
2021-06-02 00:48:24 +00:00
|
|
|
|
2024-11-04 04:18:36 +00:00
|
|
|
/*
|
|
|
|
* Pre-calculated geometry
|
|
|
|
*/
|
|
|
|
pag->block_count = __xfs_ag_block_count(mp, index, agcount, dblocks);
|
|
|
|
pag->min_block = XFS_AGFL_BLOCK(mp);
|
|
|
|
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
|
|
|
&pag->agino_max);
|
2021-06-02 00:48:24 +00:00
|
|
|
|
2024-11-04 04:18:38 +00:00
|
|
|
error = xfs_group_insert(mp, pag_group(pag), index, XG_TYPE_AG);
|
|
|
|
if (error)
|
2024-11-04 04:18:37 +00:00
|
|
|
goto out_buf_cache_destroy;
|
|
|
|
|
2021-06-02 00:48:24 +00:00
|
|
|
return 0;
|
|
|
|
|
2024-11-04 04:18:37 +00:00
|
|
|
out_buf_cache_destroy:
|
|
|
|
xfs_buf_cache_destroy(&pag->pag_bcache);
|
2024-11-04 04:18:41 +00:00
|
|
|
out_free_perag:
|
2024-01-15 22:59:43 +00:00
|
|
|
kfree(pag);
|
2024-11-04 04:18:36 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
xfs_initialize_perag(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_agnumber_t orig_agcount,
|
|
|
|
xfs_agnumber_t new_agcount,
|
|
|
|
xfs_rfsblock_t dblocks,
|
|
|
|
xfs_agnumber_t *maxagi)
|
|
|
|
{
|
|
|
|
xfs_agnumber_t index;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (orig_agcount >= new_agcount)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (index = orig_agcount; index < new_agcount; index++) {
|
|
|
|
error = xfs_perag_alloc(mp, index, new_agcount, dblocks);
|
|
|
|
if (error)
|
|
|
|
goto out_unwind_new_pags;
|
|
|
|
}
|
|
|
|
|
|
|
|
*maxagi = xfs_set_inode_alloc(mp, new_agcount);
|
|
|
|
mp->m_ag_prealloc_blocks = xfs_prealloc_blocks(mp);
|
|
|
|
return 0;
|
|
|
|
|
2021-06-02 00:48:24 +00:00
|
|
|
out_unwind_new_pags:
|
2024-11-04 04:18:36 +00:00
|
|
|
xfs_free_perag_range(mp, orig_agcount, index);
|
2021-06-02 00:48:24 +00:00
|
|
|
return error;
|
|
|
|
}
|
2018-05-14 06:10:08 +00:00
|
|
|
|
2020-01-24 01:01:17 +00:00
|
|
|
static int
|
2018-05-14 06:10:08 +00:00
|
|
|
xfs_get_aghdr_buf(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_daddr_t blkno,
|
|
|
|
size_t numblks,
|
2020-01-24 01:01:17 +00:00
|
|
|
struct xfs_buf **bpp,
|
2018-05-14 06:10:08 +00:00
|
|
|
const struct xfs_buf_ops *ops)
|
|
|
|
{
|
|
|
|
struct xfs_buf *bp;
|
2020-01-24 01:01:17 +00:00
|
|
|
int error;
|
2018-05-14 06:10:08 +00:00
|
|
|
|
2020-01-24 01:01:17 +00:00
|
|
|
error = xfs_buf_get_uncached(mp->m_ddev_targp, numblks, 0, &bp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2018-05-14 06:10:08 +00:00
|
|
|
|
|
|
|
bp->b_maps[0].bm_bn = blkno;
|
|
|
|
bp->b_ops = ops;
|
|
|
|
|
2020-01-24 01:01:17 +00:00
|
|
|
*bpp = bp;
|
|
|
|
return 0;
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generic btree root block init function
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_btroot_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
2024-02-22 20:35:17 +00:00
|
|
|
xfs_btree_init_buf(mp, bp, id->bc_ops, 0, 0, id->agno);
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 02:30:21 +00:00
|
|
|
/* Finish initializing a free space btree. */
|
2018-05-14 06:10:08 +00:00
|
|
|
static void
|
2019-06-29 02:30:21 +00:00
|
|
|
xfs_freesp_init_recs(
|
2018-05-14 06:10:08 +00:00
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
|
|
|
struct xfs_alloc_rec *arec;
|
2019-06-29 02:30:21 +00:00
|
|
|
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
|
2018-05-14 06:10:08 +00:00
|
|
|
|
|
|
|
arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1);
|
|
|
|
arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks);
|
2019-06-29 02:30:21 +00:00
|
|
|
|
2022-07-07 09:13:21 +00:00
|
|
|
if (xfs_ag_contains_log(mp, id->agno)) {
|
2019-06-29 02:30:21 +00:00
|
|
|
struct xfs_alloc_rec *nrec;
|
|
|
|
xfs_agblock_t start = XFS_FSB_TO_AGBNO(mp,
|
|
|
|
mp->m_sb.sb_logstart);
|
|
|
|
|
|
|
|
ASSERT(start >= mp->m_ag_prealloc_blocks);
|
|
|
|
if (start != mp->m_ag_prealloc_blocks) {
|
|
|
|
/*
|
xfs: set bnobt/cntbt numrecs correctly when formatting new AGs
Through generic/300, I discovered that mkfs.xfs creates corrupt
filesystems when given these parameters:
# mkfs.xfs -d size=512M /dev/sda -f -d su=128k,sw=4 --unsupported
Filesystems formatted with --unsupported are not supported!!
meta-data=/dev/sda isize=512 agcount=8, agsize=16352 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
data = bsize=4096 blocks=130816, imaxpct=25
= sunit=32 swidth=128 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=8192, version=2
= sectsz=512 sunit=32 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 blks
Discarding blocks...Done.
# xfs_repair -n /dev/sda
Phase 1 - find and verify superblock...
- reporting progress in intervals of 15 minutes
Phase 2 - using internal log
- zero log...
- 16:30:50: zeroing log - 16320 of 16320 blocks done
- scan filesystem freespace and inode maps...
agf_freeblks 25, counted 0 in ag 4
sb_fdblocks 8823, counted 8798
The root cause of this problem is the numrecs handling in
xfs_freesp_init_recs, which is used to initialize a new AG. Prior to
calling the function, we set up the new bnobt block with numrecs == 1
and rely on _freesp_init_recs to format that new record. If the last
record created has a blockcount of zero, then it sets numrecs = 0.
That last bit isn't correct if the AG contains the log, the start of the
log is not immediately after the initial blocks due to stripe alignment,
and the end of the log is perfectly aligned with the end of the AG. For
this case, we actually formatted a single bnobt record to handle the
free space before the start of the (stripe aligned) log, and incremented
arec to try to format a second record. That second record turned out to
be unnecessary, so what we really want is to leave numrecs at 1.
The numrecs handling itself is overly complicated because a different
function sets numrecs == 1. Change the bnobt creation code to start
with numrecs set to zero and only increment it after successfully
formatting a free space extent into the btree block.
Fixes: f327a00745ff ("xfs: account for log space when formatting new AGs")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2023-05-01 23:14:36 +00:00
|
|
|
* Modify first record to pad stripe align of log and
|
|
|
|
* bump the record count.
|
2019-06-29 02:30:21 +00:00
|
|
|
*/
|
|
|
|
arec->ar_blockcount = cpu_to_be32(start -
|
|
|
|
mp->m_ag_prealloc_blocks);
|
xfs: set bnobt/cntbt numrecs correctly when formatting new AGs
Through generic/300, I discovered that mkfs.xfs creates corrupt
filesystems when given these parameters:
# mkfs.xfs -d size=512M /dev/sda -f -d su=128k,sw=4 --unsupported
Filesystems formatted with --unsupported are not supported!!
meta-data=/dev/sda isize=512 agcount=8, agsize=16352 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
data = bsize=4096 blocks=130816, imaxpct=25
= sunit=32 swidth=128 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=8192, version=2
= sectsz=512 sunit=32 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 blks
Discarding blocks...Done.
# xfs_repair -n /dev/sda
Phase 1 - find and verify superblock...
- reporting progress in intervals of 15 minutes
Phase 2 - using internal log
- zero log...
- 16:30:50: zeroing log - 16320 of 16320 blocks done
- scan filesystem freespace and inode maps...
agf_freeblks 25, counted 0 in ag 4
sb_fdblocks 8823, counted 8798
The root cause of this problem is the numrecs handling in
xfs_freesp_init_recs, which is used to initialize a new AG. Prior to
calling the function, we set up the new bnobt block with numrecs == 1
and rely on _freesp_init_recs to format that new record. If the last
record created has a blockcount of zero, then it sets numrecs = 0.
That last bit isn't correct if the AG contains the log, the start of the
log is not immediately after the initial blocks due to stripe alignment,
and the end of the log is perfectly aligned with the end of the AG. For
this case, we actually formatted a single bnobt record to handle the
free space before the start of the (stripe aligned) log, and incremented
arec to try to format a second record. That second record turned out to
be unnecessary, so what we really want is to leave numrecs at 1.
The numrecs handling itself is overly complicated because a different
function sets numrecs == 1. Change the bnobt creation code to start
with numrecs set to zero and only increment it after successfully
formatting a free space extent into the btree block.
Fixes: f327a00745ff ("xfs: account for log space when formatting new AGs")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2023-05-01 23:14:36 +00:00
|
|
|
be16_add_cpu(&block->bb_numrecs, 1);
|
2019-06-29 02:30:21 +00:00
|
|
|
nrec = arec + 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Insert second record at start of internal log
|
|
|
|
* which then gets trimmed.
|
|
|
|
*/
|
|
|
|
nrec->ar_startblock = cpu_to_be32(
|
|
|
|
be32_to_cpu(arec->ar_startblock) +
|
|
|
|
be32_to_cpu(arec->ar_blockcount));
|
|
|
|
arec = nrec;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Change record start to after the internal log
|
|
|
|
*/
|
|
|
|
be32_add_cpu(&arec->ar_startblock, mp->m_sb.sb_logblocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
xfs: set bnobt/cntbt numrecs correctly when formatting new AGs
Through generic/300, I discovered that mkfs.xfs creates corrupt
filesystems when given these parameters:
# mkfs.xfs -d size=512M /dev/sda -f -d su=128k,sw=4 --unsupported
Filesystems formatted with --unsupported are not supported!!
meta-data=/dev/sda isize=512 agcount=8, agsize=16352 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
data = bsize=4096 blocks=130816, imaxpct=25
= sunit=32 swidth=128 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=8192, version=2
= sectsz=512 sunit=32 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 blks
Discarding blocks...Done.
# xfs_repair -n /dev/sda
Phase 1 - find and verify superblock...
- reporting progress in intervals of 15 minutes
Phase 2 - using internal log
- zero log...
- 16:30:50: zeroing log - 16320 of 16320 blocks done
- scan filesystem freespace and inode maps...
agf_freeblks 25, counted 0 in ag 4
sb_fdblocks 8823, counted 8798
The root cause of this problem is the numrecs handling in
xfs_freesp_init_recs, which is used to initialize a new AG. Prior to
calling the function, we set up the new bnobt block with numrecs == 1
and rely on _freesp_init_recs to format that new record. If the last
record created has a blockcount of zero, then it sets numrecs = 0.
That last bit isn't correct if the AG contains the log, the start of the
log is not immediately after the initial blocks due to stripe alignment,
and the end of the log is perfectly aligned with the end of the AG. For
this case, we actually formatted a single bnobt record to handle the
free space before the start of the (stripe aligned) log, and incremented
arec to try to format a second record. That second record turned out to
be unnecessary, so what we really want is to leave numrecs at 1.
The numrecs handling itself is overly complicated because a different
function sets numrecs == 1. Change the bnobt creation code to start
with numrecs set to zero and only increment it after successfully
formatting a free space extent into the btree block.
Fixes: f327a00745ff ("xfs: account for log space when formatting new AGs")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2023-05-01 23:14:36 +00:00
|
|
|
* Calculate the block count of this record; if it is nonzero,
|
|
|
|
* increment the record count.
|
2019-06-29 02:30:21 +00:00
|
|
|
*/
|
2018-05-14 06:10:08 +00:00
|
|
|
arec->ar_blockcount = cpu_to_be32(id->agsize -
|
|
|
|
be32_to_cpu(arec->ar_startblock));
|
xfs: set bnobt/cntbt numrecs correctly when formatting new AGs
Through generic/300, I discovered that mkfs.xfs creates corrupt
filesystems when given these parameters:
# mkfs.xfs -d size=512M /dev/sda -f -d su=128k,sw=4 --unsupported
Filesystems formatted with --unsupported are not supported!!
meta-data=/dev/sda isize=512 agcount=8, agsize=16352 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
data = bsize=4096 blocks=130816, imaxpct=25
= sunit=32 swidth=128 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=8192, version=2
= sectsz=512 sunit=32 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 blks
Discarding blocks...Done.
# xfs_repair -n /dev/sda
Phase 1 - find and verify superblock...
- reporting progress in intervals of 15 minutes
Phase 2 - using internal log
- zero log...
- 16:30:50: zeroing log - 16320 of 16320 blocks done
- scan filesystem freespace and inode maps...
agf_freeblks 25, counted 0 in ag 4
sb_fdblocks 8823, counted 8798
The root cause of this problem is the numrecs handling in
xfs_freesp_init_recs, which is used to initialize a new AG. Prior to
calling the function, we set up the new bnobt block with numrecs == 1
and rely on _freesp_init_recs to format that new record. If the last
record created has a blockcount of zero, then it sets numrecs = 0.
That last bit isn't correct if the AG contains the log, the start of the
log is not immediately after the initial blocks due to stripe alignment,
and the end of the log is perfectly aligned with the end of the AG. For
this case, we actually formatted a single bnobt record to handle the
free space before the start of the (stripe aligned) log, and incremented
arec to try to format a second record. That second record turned out to
be unnecessary, so what we really want is to leave numrecs at 1.
The numrecs handling itself is overly complicated because a different
function sets numrecs == 1. Change the bnobt creation code to start
with numrecs set to zero and only increment it after successfully
formatting a free space extent into the btree block.
Fixes: f327a00745ff ("xfs: account for log space when formatting new AGs")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2023-05-01 23:14:36 +00:00
|
|
|
if (arec->ar_blockcount)
|
|
|
|
be16_add_cpu(&block->bb_numrecs, 1);
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 02:30:21 +00:00
|
|
|
/*
|
2024-02-22 20:35:16 +00:00
|
|
|
* bnobt/cntbt btree root block init functions
|
2019-06-29 02:30:21 +00:00
|
|
|
*/
|
2018-05-14 06:10:08 +00:00
|
|
|
static void
|
2019-06-29 02:30:21 +00:00
|
|
|
xfs_bnoroot_init(
|
2018-05-14 06:10:08 +00:00
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
2024-02-22 20:35:17 +00:00
|
|
|
xfs_btree_init_buf(mp, bp, id->bc_ops, 0, 0, id->agno);
|
2019-06-29 02:30:21 +00:00
|
|
|
xfs_freesp_init_recs(mp, bp, id);
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reverse map root block init
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_rmaproot_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
|
|
|
struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp);
|
|
|
|
struct xfs_rmap_rec *rrec;
|
|
|
|
|
2024-02-22 20:35:17 +00:00
|
|
|
xfs_btree_init_buf(mp, bp, id->bc_ops, 0, 4, id->agno);
|
2018-05-14 06:10:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* mark the AG header regions as static metadata The BNO
|
|
|
|
* btree block is the first block after the headers, so
|
|
|
|
* it's location defines the size of region the static
|
|
|
|
* metadata consumes.
|
|
|
|
*
|
|
|
|
* Note: unlike mkfs, we never have to account for log
|
|
|
|
* space when growing the data regions
|
|
|
|
*/
|
|
|
|
rrec = XFS_RMAP_REC_ADDR(block, 1);
|
|
|
|
rrec->rm_startblock = 0;
|
|
|
|
rrec->rm_blockcount = cpu_to_be32(XFS_BNO_BLOCK(mp));
|
|
|
|
rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_FS);
|
|
|
|
rrec->rm_offset = 0;
|
|
|
|
|
|
|
|
/* account freespace btree root blocks */
|
|
|
|
rrec = XFS_RMAP_REC_ADDR(block, 2);
|
|
|
|
rrec->rm_startblock = cpu_to_be32(XFS_BNO_BLOCK(mp));
|
|
|
|
rrec->rm_blockcount = cpu_to_be32(2);
|
|
|
|
rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
|
|
|
|
rrec->rm_offset = 0;
|
|
|
|
|
|
|
|
/* account inode btree root blocks */
|
|
|
|
rrec = XFS_RMAP_REC_ADDR(block, 3);
|
|
|
|
rrec->rm_startblock = cpu_to_be32(XFS_IBT_BLOCK(mp));
|
|
|
|
rrec->rm_blockcount = cpu_to_be32(XFS_RMAP_BLOCK(mp) -
|
|
|
|
XFS_IBT_BLOCK(mp));
|
|
|
|
rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_INOBT);
|
|
|
|
rrec->rm_offset = 0;
|
|
|
|
|
|
|
|
/* account for rmap btree root */
|
|
|
|
rrec = XFS_RMAP_REC_ADDR(block, 4);
|
|
|
|
rrec->rm_startblock = cpu_to_be32(XFS_RMAP_BLOCK(mp));
|
|
|
|
rrec->rm_blockcount = cpu_to_be32(1);
|
|
|
|
rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_AG);
|
|
|
|
rrec->rm_offset = 0;
|
|
|
|
|
|
|
|
/* account for refc btree root */
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_reflink(mp)) {
|
2018-05-14 06:10:08 +00:00
|
|
|
rrec = XFS_RMAP_REC_ADDR(block, 5);
|
|
|
|
rrec->rm_startblock = cpu_to_be32(xfs_refc_block(mp));
|
|
|
|
rrec->rm_blockcount = cpu_to_be32(1);
|
|
|
|
rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
|
|
|
|
rrec->rm_offset = 0;
|
|
|
|
be16_add_cpu(&block->bb_numrecs, 1);
|
|
|
|
}
|
2019-06-29 02:30:21 +00:00
|
|
|
|
|
|
|
/* account for the log space */
|
2022-07-07 09:13:21 +00:00
|
|
|
if (xfs_ag_contains_log(mp, id->agno)) {
|
2019-06-29 02:30:21 +00:00
|
|
|
rrec = XFS_RMAP_REC_ADDR(block,
|
|
|
|
be16_to_cpu(block->bb_numrecs) + 1);
|
|
|
|
rrec->rm_startblock = cpu_to_be32(
|
|
|
|
XFS_FSB_TO_AGBNO(mp, mp->m_sb.sb_logstart));
|
|
|
|
rrec->rm_blockcount = cpu_to_be32(mp->m_sb.sb_logblocks);
|
|
|
|
rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_LOG);
|
|
|
|
rrec->rm_offset = 0;
|
|
|
|
be16_add_cpu(&block->bb_numrecs, 1);
|
|
|
|
}
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise new secondary superblocks with the pre-grow geometry, but mark
|
|
|
|
* them as "in progress" so we know they haven't yet been activated. This will
|
|
|
|
* get cleared when the update with the new geometry information is done after
|
|
|
|
* changes to the primary are committed. This isn't strictly necessary, but we
|
|
|
|
* get it for free with the delayed buffer write lists and it means we can tell
|
|
|
|
* if a grow operation didn't complete properly after the fact.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_sbblock_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
2020-03-10 15:57:30 +00:00
|
|
|
struct xfs_dsb *dsb = bp->b_addr;
|
2018-05-14 06:10:08 +00:00
|
|
|
|
|
|
|
xfs_sb_to_disk(dsb, &mp->m_sb);
|
|
|
|
dsb->sb_inprogress = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xfs_agfblock_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
2020-03-10 15:57:29 +00:00
|
|
|
struct xfs_agf *agf = bp->b_addr;
|
2018-05-14 06:10:08 +00:00
|
|
|
xfs_extlen_t tmpsize;
|
|
|
|
|
|
|
|
agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC);
|
|
|
|
agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION);
|
|
|
|
agf->agf_seqno = cpu_to_be32(id->agno);
|
|
|
|
agf->agf_length = cpu_to_be32(id->agsize);
|
2024-02-22 20:39:46 +00:00
|
|
|
agf->agf_bno_root = cpu_to_be32(XFS_BNO_BLOCK(mp));
|
|
|
|
agf->agf_cnt_root = cpu_to_be32(XFS_CNT_BLOCK(mp));
|
|
|
|
agf->agf_bno_level = cpu_to_be32(1);
|
|
|
|
agf->agf_cnt_level = cpu_to_be32(1);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_rmapbt(mp)) {
|
2024-02-22 20:39:46 +00:00
|
|
|
agf->agf_rmap_root = cpu_to_be32(XFS_RMAP_BLOCK(mp));
|
|
|
|
agf->agf_rmap_level = cpu_to_be32(1);
|
2018-05-14 06:10:08 +00:00
|
|
|
agf->agf_rmap_blocks = cpu_to_be32(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
agf->agf_flfirst = cpu_to_be32(1);
|
|
|
|
agf->agf_fllast = 0;
|
|
|
|
agf->agf_flcount = 0;
|
|
|
|
tmpsize = id->agsize - mp->m_ag_prealloc_blocks;
|
|
|
|
agf->agf_freeblks = cpu_to_be32(tmpsize);
|
|
|
|
agf->agf_longest = cpu_to_be32(tmpsize);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_crc(mp))
|
2018-05-14 06:10:08 +00:00
|
|
|
uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_reflink(mp)) {
|
2018-05-14 06:10:08 +00:00
|
|
|
agf->agf_refcount_root = cpu_to_be32(
|
|
|
|
xfs_refc_block(mp));
|
|
|
|
agf->agf_refcount_level = cpu_to_be32(1);
|
|
|
|
agf->agf_refcount_blocks = cpu_to_be32(1);
|
|
|
|
}
|
2019-06-29 02:30:21 +00:00
|
|
|
|
2022-07-07 09:13:21 +00:00
|
|
|
if (xfs_ag_contains_log(mp, id->agno)) {
|
2019-06-29 02:30:21 +00:00
|
|
|
int64_t logblocks = mp->m_sb.sb_logblocks;
|
|
|
|
|
|
|
|
be32_add_cpu(&agf->agf_freeblks, -logblocks);
|
|
|
|
agf->agf_longest = cpu_to_be32(id->agsize -
|
|
|
|
XFS_FSB_TO_AGBNO(mp, mp->m_sb.sb_logstart) - logblocks);
|
|
|
|
}
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xfs_agflblock_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
|
|
|
struct xfs_agfl *agfl = XFS_BUF_TO_AGFL(bp);
|
|
|
|
__be32 *agfl_bno;
|
|
|
|
int bucket;
|
|
|
|
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_crc(mp)) {
|
2018-05-14 06:10:08 +00:00
|
|
|
agfl->agfl_magicnum = cpu_to_be32(XFS_AGFL_MAGIC);
|
|
|
|
agfl->agfl_seqno = cpu_to_be32(id->agno);
|
|
|
|
uuid_copy(&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid);
|
|
|
|
}
|
|
|
|
|
2020-03-10 15:57:28 +00:00
|
|
|
agfl_bno = xfs_buf_to_agfl_bno(bp);
|
2018-05-14 06:10:08 +00:00
|
|
|
for (bucket = 0; bucket < xfs_agfl_size(mp); bucket++)
|
|
|
|
agfl_bno[bucket] = cpu_to_be32(NULLAGBLOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xfs_agiblock_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
{
|
2020-03-10 15:57:29 +00:00
|
|
|
struct xfs_agi *agi = bp->b_addr;
|
2018-05-14 06:10:08 +00:00
|
|
|
int bucket;
|
|
|
|
|
|
|
|
agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC);
|
|
|
|
agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION);
|
|
|
|
agi->agi_seqno = cpu_to_be32(id->agno);
|
|
|
|
agi->agi_length = cpu_to_be32(id->agsize);
|
|
|
|
agi->agi_count = 0;
|
|
|
|
agi->agi_root = cpu_to_be32(XFS_IBT_BLOCK(mp));
|
|
|
|
agi->agi_level = cpu_to_be32(1);
|
|
|
|
agi->agi_freecount = 0;
|
|
|
|
agi->agi_newino = cpu_to_be32(NULLAGINO);
|
|
|
|
agi->agi_dirino = cpu_to_be32(NULLAGINO);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_crc(mp))
|
2018-05-14 06:10:08 +00:00
|
|
|
uuid_copy(&agi->agi_uuid, &mp->m_sb.sb_meta_uuid);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_finobt(mp)) {
|
2018-05-14 06:10:08 +00:00
|
|
|
agi->agi_free_root = cpu_to_be32(XFS_FIBT_BLOCK(mp));
|
|
|
|
agi->agi_free_level = cpu_to_be32(1);
|
|
|
|
}
|
|
|
|
for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
|
|
|
|
agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
|
2021-08-19 01:46:55 +00:00
|
|
|
if (xfs_has_inobtcounts(mp)) {
|
2020-08-17 16:58:01 +00:00
|
|
|
agi->agi_iblocks = cpu_to_be32(1);
|
2021-08-19 01:46:55 +00:00
|
|
|
if (xfs_has_finobt(mp))
|
2020-08-17 16:58:01 +00:00
|
|
|
agi->agi_fblocks = cpu_to_be32(1);
|
|
|
|
}
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef void (*aghdr_init_work_f)(struct xfs_mount *mp, struct xfs_buf *bp,
|
|
|
|
struct aghdr_init_data *id);
|
|
|
|
static int
|
|
|
|
xfs_ag_init_hdr(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct aghdr_init_data *id,
|
|
|
|
aghdr_init_work_f work,
|
|
|
|
const struct xfs_buf_ops *ops)
|
|
|
|
{
|
|
|
|
struct xfs_buf *bp;
|
2020-01-24 01:01:17 +00:00
|
|
|
int error;
|
2018-05-14 06:10:08 +00:00
|
|
|
|
2020-01-24 01:01:17 +00:00
|
|
|
error = xfs_get_aghdr_buf(mp, id->daddr, id->numblks, &bp, ops);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2018-05-14 06:10:08 +00:00
|
|
|
|
|
|
|
(*work)(mp, bp, id);
|
|
|
|
|
|
|
|
xfs_buf_delwri_queue(bp, &id->buffer_list);
|
|
|
|
xfs_buf_relse(bp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct xfs_aghdr_grow_data {
|
|
|
|
xfs_daddr_t daddr;
|
|
|
|
size_t numblks;
|
|
|
|
const struct xfs_buf_ops *ops;
|
|
|
|
aghdr_init_work_f work;
|
2024-02-22 20:35:16 +00:00
|
|
|
const struct xfs_btree_ops *bc_ops;
|
2018-05-14 06:10:08 +00:00
|
|
|
bool need_init;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare new AG headers to be written to disk. We use uncached buffers here,
|
|
|
|
* as it is assumed these new AG headers are currently beyond the currently
|
|
|
|
* valid filesystem address space. Using cached buffers would trip over EOFS
|
|
|
|
* corruption detection alogrithms in the buffer cache lookup routines.
|
|
|
|
*
|
|
|
|
* This is a non-transactional function, but the prepared buffers are added to a
|
|
|
|
* delayed write buffer list supplied by the caller so they can submit them to
|
|
|
|
* disk and wait on them as required.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_ag_init_headers(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct aghdr_init_data *id)
|
|
|
|
|
|
|
|
{
|
|
|
|
struct xfs_aghdr_grow_data aghdr_data[] = {
|
|
|
|
{ /* SB */
|
|
|
|
.daddr = XFS_AG_DADDR(mp, id->agno, XFS_SB_DADDR),
|
|
|
|
.numblks = XFS_FSS_TO_BB(mp, 1),
|
|
|
|
.ops = &xfs_sb_buf_ops,
|
|
|
|
.work = &xfs_sbblock_init,
|
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* AGF */
|
|
|
|
.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGF_DADDR(mp)),
|
|
|
|
.numblks = XFS_FSS_TO_BB(mp, 1),
|
|
|
|
.ops = &xfs_agf_buf_ops,
|
|
|
|
.work = &xfs_agfblock_init,
|
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* AGFL */
|
|
|
|
.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGFL_DADDR(mp)),
|
|
|
|
.numblks = XFS_FSS_TO_BB(mp, 1),
|
|
|
|
.ops = &xfs_agfl_buf_ops,
|
|
|
|
.work = &xfs_agflblock_init,
|
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* AGI */
|
|
|
|
.daddr = XFS_AG_DADDR(mp, id->agno, XFS_AGI_DADDR(mp)),
|
|
|
|
.numblks = XFS_FSS_TO_BB(mp, 1),
|
|
|
|
.ops = &xfs_agi_buf_ops,
|
|
|
|
.work = &xfs_agiblock_init,
|
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* BNO root block */
|
|
|
|
.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_BNO_BLOCK(mp)),
|
|
|
|
.numblks = BTOBB(mp->m_sb.sb_blocksize),
|
2019-02-07 18:45:47 +00:00
|
|
|
.ops = &xfs_bnobt_buf_ops,
|
2018-05-14 06:10:08 +00:00
|
|
|
.work = &xfs_bnoroot_init,
|
2024-02-22 20:35:16 +00:00
|
|
|
.bc_ops = &xfs_bnobt_ops,
|
2018-05-14 06:10:08 +00:00
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* CNT root block */
|
|
|
|
.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_CNT_BLOCK(mp)),
|
|
|
|
.numblks = BTOBB(mp->m_sb.sb_blocksize),
|
2019-02-07 18:45:47 +00:00
|
|
|
.ops = &xfs_cntbt_buf_ops,
|
2024-02-22 20:35:16 +00:00
|
|
|
.work = &xfs_bnoroot_init,
|
|
|
|
.bc_ops = &xfs_cntbt_ops,
|
2018-05-14 06:10:08 +00:00
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* INO root block */
|
|
|
|
.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_IBT_BLOCK(mp)),
|
|
|
|
.numblks = BTOBB(mp->m_sb.sb_blocksize),
|
|
|
|
.ops = &xfs_inobt_buf_ops,
|
|
|
|
.work = &xfs_btroot_init,
|
2024-02-22 20:35:16 +00:00
|
|
|
.bc_ops = &xfs_inobt_ops,
|
2018-05-14 06:10:08 +00:00
|
|
|
.need_init = true
|
|
|
|
},
|
|
|
|
{ /* FINO root block */
|
|
|
|
.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_FIBT_BLOCK(mp)),
|
|
|
|
.numblks = BTOBB(mp->m_sb.sb_blocksize),
|
2019-02-07 18:45:46 +00:00
|
|
|
.ops = &xfs_finobt_buf_ops,
|
2018-05-14 06:10:08 +00:00
|
|
|
.work = &xfs_btroot_init,
|
2024-02-22 20:35:16 +00:00
|
|
|
.bc_ops = &xfs_finobt_ops,
|
2021-08-19 01:46:37 +00:00
|
|
|
.need_init = xfs_has_finobt(mp)
|
2018-05-14 06:10:08 +00:00
|
|
|
},
|
|
|
|
{ /* RMAP root block */
|
|
|
|
.daddr = XFS_AGB_TO_DADDR(mp, id->agno, XFS_RMAP_BLOCK(mp)),
|
|
|
|
.numblks = BTOBB(mp->m_sb.sb_blocksize),
|
|
|
|
.ops = &xfs_rmapbt_buf_ops,
|
|
|
|
.work = &xfs_rmaproot_init,
|
2024-02-22 20:35:16 +00:00
|
|
|
.bc_ops = &xfs_rmapbt_ops,
|
2021-08-19 01:46:37 +00:00
|
|
|
.need_init = xfs_has_rmapbt(mp)
|
2018-05-14 06:10:08 +00:00
|
|
|
},
|
|
|
|
{ /* REFC root block */
|
|
|
|
.daddr = XFS_AGB_TO_DADDR(mp, id->agno, xfs_refc_block(mp)),
|
|
|
|
.numblks = BTOBB(mp->m_sb.sb_blocksize),
|
|
|
|
.ops = &xfs_refcountbt_buf_ops,
|
|
|
|
.work = &xfs_btroot_init,
|
2024-02-22 20:35:16 +00:00
|
|
|
.bc_ops = &xfs_refcountbt_ops,
|
2021-08-19 01:46:37 +00:00
|
|
|
.need_init = xfs_has_reflink(mp)
|
2018-05-14 06:10:08 +00:00
|
|
|
},
|
|
|
|
{ /* NULL terminating block */
|
|
|
|
.daddr = XFS_BUF_DADDR_NULL,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
struct xfs_aghdr_grow_data *dp;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
/* Account for AG free space in new AG */
|
|
|
|
id->nfree += id->agsize - mp->m_ag_prealloc_blocks;
|
|
|
|
for (dp = &aghdr_data[0]; dp->daddr != XFS_BUF_DADDR_NULL; dp++) {
|
|
|
|
if (!dp->need_init)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
id->daddr = dp->daddr;
|
|
|
|
id->numblks = dp->numblks;
|
2024-02-22 20:35:16 +00:00
|
|
|
id->bc_ops = dp->bc_ops;
|
2018-05-14 06:10:08 +00:00
|
|
|
error = xfs_ag_init_hdr(mp, id, dp->work, dp->ops);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
2018-05-14 06:10:08 +00:00
|
|
|
|
2021-03-24 02:05:38 +00:00
|
|
|
int
|
|
|
|
xfs_ag_shrink_space(
|
2022-07-07 09:07:09 +00:00
|
|
|
struct xfs_perag *pag,
|
2021-03-24 02:05:38 +00:00
|
|
|
struct xfs_trans **tpp,
|
|
|
|
xfs_extlen_t delta)
|
|
|
|
{
|
2024-11-04 04:18:38 +00:00
|
|
|
struct xfs_mount *mp = pag_mount(pag);
|
2021-03-24 02:05:38 +00:00
|
|
|
struct xfs_alloc_arg args = {
|
|
|
|
.tp = *tpp,
|
|
|
|
.mp = mp,
|
2023-02-12 22:14:53 +00:00
|
|
|
.pag = pag,
|
2021-03-24 02:05:38 +00:00
|
|
|
.minlen = delta,
|
|
|
|
.maxlen = delta,
|
|
|
|
.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE,
|
|
|
|
.resv = XFS_AG_RESV_NONE,
|
|
|
|
.prod = 1
|
|
|
|
};
|
|
|
|
struct xfs_buf *agibp, *agfbp;
|
|
|
|
struct xfs_agi *agi;
|
|
|
|
struct xfs_agf *agf;
|
2021-06-22 00:39:09 +00:00
|
|
|
xfs_agblock_t aglen;
|
2021-03-24 02:05:38 +00:00
|
|
|
int error, err2;
|
|
|
|
|
2024-11-04 04:18:38 +00:00
|
|
|
ASSERT(pag_agno(pag) == mp->m_sb.sb_agcount - 1);
|
2024-04-15 21:54:03 +00:00
|
|
|
error = xfs_ialloc_read_agi(pag, *tpp, 0, &agibp);
|
2021-03-24 02:05:38 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
agi = agibp->b_addr;
|
|
|
|
|
2022-07-07 09:07:40 +00:00
|
|
|
error = xfs_alloc_read_agf(pag, *tpp, 0, &agfbp);
|
2021-03-24 02:05:38 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
agf = agfbp->b_addr;
|
2021-06-22 00:39:09 +00:00
|
|
|
aglen = be32_to_cpu(agi->agi_length);
|
2021-03-24 02:05:38 +00:00
|
|
|
/* some extra paranoid checks before we shrink the ag */
|
2024-02-22 20:32:55 +00:00
|
|
|
if (XFS_IS_CORRUPT(mp, agf->agf_length != agi->agi_length)) {
|
|
|
|
xfs_ag_mark_sick(pag, XFS_SICK_AG_AGF);
|
2021-03-24 02:05:38 +00:00
|
|
|
return -EFSCORRUPTED;
|
2024-02-22 20:32:55 +00:00
|
|
|
}
|
2021-06-22 00:39:09 +00:00
|
|
|
if (delta >= aglen)
|
2021-03-24 02:05:38 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-07-12 19:58:47 +00:00
|
|
|
/*
|
|
|
|
* Make sure that the last inode cluster cannot overlap with the new
|
|
|
|
* end of the AG, even if it's sparse.
|
|
|
|
*/
|
2023-02-12 22:14:52 +00:00
|
|
|
error = xfs_ialloc_check_shrink(pag, *tpp, agibp, aglen - delta);
|
2021-07-12 19:58:47 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2021-03-24 02:05:38 +00:00
|
|
|
/*
|
|
|
|
* Disable perag reservations so it doesn't cause the allocation request
|
|
|
|
* to fail. We'll reestablish reservation before we return.
|
|
|
|
*/
|
2024-04-22 11:20:12 +00:00
|
|
|
xfs_ag_resv_free(pag);
|
2021-03-24 02:05:38 +00:00
|
|
|
|
|
|
|
/* internal log shouldn't also show up in the free space btrees */
|
2023-02-12 22:14:54 +00:00
|
|
|
error = xfs_alloc_vextent_exact_bno(&args,
|
2024-11-04 04:18:28 +00:00
|
|
|
xfs_agbno_to_fsb(pag, aglen - delta));
|
2021-03-24 02:05:38 +00:00
|
|
|
if (!error && args.agbno == NULLAGBLOCK)
|
|
|
|
error = -ENOSPC;
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
/*
|
2024-03-06 01:12:46 +00:00
|
|
|
* If extent allocation fails, need to roll the transaction to
|
2021-03-24 02:05:38 +00:00
|
|
|
* ensure that the AGFL fixup has been committed anyway.
|
2024-03-06 01:12:46 +00:00
|
|
|
*
|
|
|
|
* We need to hold the AGF across the roll to ensure nothing can
|
|
|
|
* access the AG for allocation until the shrink is fully
|
|
|
|
* cleaned up. And due to the resetting of the AG block
|
|
|
|
* reservation space needing to lock the AGI, we also have to
|
|
|
|
* hold that so we don't get AGI/AGF lock order inversions in
|
|
|
|
* the error handling path.
|
2021-03-24 02:05:38 +00:00
|
|
|
*/
|
|
|
|
xfs_trans_bhold(*tpp, agfbp);
|
2024-03-06 01:12:46 +00:00
|
|
|
xfs_trans_bhold(*tpp, agibp);
|
2021-03-24 02:05:38 +00:00
|
|
|
err2 = xfs_trans_roll(tpp);
|
|
|
|
if (err2)
|
|
|
|
return err2;
|
|
|
|
xfs_trans_bjoin(*tpp, agfbp);
|
2024-03-06 01:12:46 +00:00
|
|
|
xfs_trans_bjoin(*tpp, agibp);
|
2021-03-24 02:05:38 +00:00
|
|
|
goto resv_init_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if successfully deleted from freespace btrees, need to confirm
|
|
|
|
* per-AG reservation works as expected.
|
|
|
|
*/
|
|
|
|
be32_add_cpu(&agi->agi_length, -delta);
|
|
|
|
be32_add_cpu(&agf->agf_length, -delta);
|
|
|
|
|
2022-07-07 09:07:24 +00:00
|
|
|
err2 = xfs_ag_resv_init(pag, *tpp);
|
2021-03-24 02:05:38 +00:00
|
|
|
if (err2) {
|
|
|
|
be32_add_cpu(&agi->agi_length, delta);
|
|
|
|
be32_add_cpu(&agf->agf_length, delta);
|
|
|
|
if (err2 != -ENOSPC)
|
|
|
|
goto resv_err;
|
|
|
|
|
2023-12-07 02:40:57 +00:00
|
|
|
err2 = xfs_free_extent_later(*tpp, args.fsbno, delta, NULL,
|
2024-07-02 18:22:51 +00:00
|
|
|
XFS_AG_RESV_NONE, XFS_FREE_EXTENT_SKIP_DISCARD);
|
2023-06-05 04:48:15 +00:00
|
|
|
if (err2)
|
|
|
|
goto resv_err;
|
2021-03-24 02:05:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Roll the transaction before trying to re-init the per-ag
|
|
|
|
* reservation. The new transaction is clean so it will cancel
|
|
|
|
* without any side effects.
|
|
|
|
*/
|
|
|
|
error = xfs_defer_finish(tpp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = -ENOSPC;
|
|
|
|
goto resv_init_out;
|
|
|
|
}
|
2023-10-11 19:35:20 +00:00
|
|
|
|
|
|
|
/* Update perag geometry */
|
|
|
|
pag->block_count -= delta;
|
2024-11-04 04:18:38 +00:00
|
|
|
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
|
|
|
&pag->agino_max);
|
2023-10-11 19:35:20 +00:00
|
|
|
|
2021-03-24 02:05:38 +00:00
|
|
|
xfs_ialloc_log_agi(*tpp, agibp, XFS_AGI_LENGTH);
|
|
|
|
xfs_alloc_log_agf(*tpp, agfbp, XFS_AGF_LENGTH);
|
|
|
|
return 0;
|
2022-07-07 09:07:24 +00:00
|
|
|
|
2021-03-24 02:05:38 +00:00
|
|
|
resv_init_out:
|
2022-07-07 09:07:24 +00:00
|
|
|
err2 = xfs_ag_resv_init(pag, *tpp);
|
2021-03-24 02:05:38 +00:00
|
|
|
if (!err2)
|
|
|
|
return error;
|
|
|
|
resv_err:
|
|
|
|
xfs_warn(mp, "Error %d reserving per-AG metadata reserve pool.", err2);
|
|
|
|
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
|
|
|
|
return err2;
|
|
|
|
}
|
|
|
|
|
2018-05-14 06:10:08 +00:00
|
|
|
/*
|
|
|
|
* Extent the AG indicated by the @id by the length passed in
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_ag_extend_space(
|
2022-07-07 09:07:09 +00:00
|
|
|
struct xfs_perag *pag,
|
2018-05-14 06:10:08 +00:00
|
|
|
struct xfs_trans *tp,
|
|
|
|
xfs_extlen_t len)
|
|
|
|
{
|
2024-11-04 04:18:38 +00:00
|
|
|
struct xfs_mount *mp = pag_mount(pag);
|
2018-05-14 06:10:08 +00:00
|
|
|
struct xfs_buf *bp;
|
|
|
|
struct xfs_agi *agi;
|
|
|
|
struct xfs_agf *agf;
|
|
|
|
int error;
|
|
|
|
|
2024-11-04 04:18:38 +00:00
|
|
|
ASSERT(pag_agno(pag) == mp->m_sb.sb_agcount - 1);
|
2022-07-07 09:07:09 +00:00
|
|
|
|
2024-04-15 21:54:03 +00:00
|
|
|
error = xfs_ialloc_read_agi(pag, tp, 0, &bp);
|
2018-05-14 06:10:08 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2020-03-10 15:57:29 +00:00
|
|
|
agi = bp->b_addr;
|
2018-05-14 06:10:08 +00:00
|
|
|
be32_add_cpu(&agi->agi_length, len);
|
|
|
|
xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Change agf length.
|
|
|
|
*/
|
2022-07-07 09:07:40 +00:00
|
|
|
error = xfs_alloc_read_agf(pag, tp, 0, &bp);
|
2018-05-14 06:10:08 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2020-03-10 15:57:29 +00:00
|
|
|
agf = bp->b_addr;
|
2018-05-14 06:10:08 +00:00
|
|
|
be32_add_cpu(&agf->agf_length, len);
|
|
|
|
ASSERT(agf->agf_length == agi->agi_length);
|
|
|
|
xfs_alloc_log_agf(tp, bp, XFS_AGF_LENGTH);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the new space.
|
|
|
|
*
|
2018-12-12 16:46:23 +00:00
|
|
|
* XFS_RMAP_OINFO_SKIP_UPDATE is used here to tell the rmap btree that
|
2018-05-14 06:10:08 +00:00
|
|
|
* this doesn't actually exist in the rmap btree.
|
|
|
|
*/
|
2022-07-07 09:07:09 +00:00
|
|
|
error = xfs_rmap_free(tp, bp, pag, be32_to_cpu(agf->agf_length) - len,
|
2018-12-12 16:46:23 +00:00
|
|
|
len, &XFS_RMAP_OINFO_SKIP_UPDATE);
|
2018-05-14 06:10:08 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2023-04-12 01:59:53 +00:00
|
|
|
error = xfs_free_extent(tp, pag, be32_to_cpu(agf->agf_length) - len,
|
|
|
|
len, &XFS_RMAP_OINFO_SKIP_UPDATE, XFS_AG_RESV_NONE);
|
2022-07-07 09:13:02 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/* Update perag geometry */
|
|
|
|
pag->block_count = be32_to_cpu(agf->agf_length);
|
2024-11-04 04:18:38 +00:00
|
|
|
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
|
|
|
&pag->agino_max);
|
2022-07-07 09:13:02 +00:00
|
|
|
return 0;
|
2018-05-14 06:10:08 +00:00
|
|
|
}
|
2019-04-12 14:41:17 +00:00
|
|
|
|
|
|
|
/* Retrieve AG geometry. */
|
|
|
|
int
|
|
|
|
xfs_ag_get_geometry(
|
2022-07-07 09:07:09 +00:00
|
|
|
struct xfs_perag *pag,
|
2019-04-12 14:41:17 +00:00
|
|
|
struct xfs_ag_geometry *ageo)
|
|
|
|
{
|
|
|
|
struct xfs_buf *agi_bp;
|
|
|
|
struct xfs_buf *agf_bp;
|
|
|
|
struct xfs_agi *agi;
|
|
|
|
struct xfs_agf *agf;
|
|
|
|
unsigned int freeblks;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/* Lock the AG headers. */
|
2024-04-15 21:54:03 +00:00
|
|
|
error = xfs_ialloc_read_agi(pag, NULL, 0, &agi_bp);
|
2019-04-12 14:41:17 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2022-07-07 09:07:40 +00:00
|
|
|
error = xfs_alloc_read_agf(pag, NULL, 0, &agf_bp);
|
2019-04-12 14:41:17 +00:00
|
|
|
if (error)
|
|
|
|
goto out_agi;
|
2020-07-13 16:13:00 +00:00
|
|
|
|
2019-04-12 14:41:17 +00:00
|
|
|
/* Fill out form. */
|
|
|
|
memset(ageo, 0, sizeof(*ageo));
|
2024-11-04 04:18:38 +00:00
|
|
|
ageo->ag_number = pag_agno(pag);
|
2019-04-12 14:41:17 +00:00
|
|
|
|
2020-03-10 15:57:29 +00:00
|
|
|
agi = agi_bp->b_addr;
|
2019-04-12 14:41:17 +00:00
|
|
|
ageo->ag_icount = be32_to_cpu(agi->agi_count);
|
|
|
|
ageo->ag_ifree = be32_to_cpu(agi->agi_freecount);
|
|
|
|
|
2020-03-10 15:57:29 +00:00
|
|
|
agf = agf_bp->b_addr;
|
2019-04-12 14:41:17 +00:00
|
|
|
ageo->ag_length = be32_to_cpu(agf->agf_length);
|
|
|
|
freeblks = pag->pagf_freeblks +
|
|
|
|
pag->pagf_flcount +
|
|
|
|
pag->pagf_btreeblks -
|
|
|
|
xfs_ag_resv_needed(pag, XFS_AG_RESV_NONE);
|
|
|
|
ageo->ag_freeblks = freeblks;
|
2019-04-12 14:41:18 +00:00
|
|
|
xfs_ag_geom_health(pag, ageo);
|
2019-04-12 14:41:17 +00:00
|
|
|
|
|
|
|
/* Release resources. */
|
|
|
|
xfs_buf_relse(agf_bp);
|
|
|
|
out_agi:
|
|
|
|
xfs_buf_relse(agi_bp);
|
|
|
|
return error;
|
|
|
|
}
|