XFS bug fixes for 6.12-rc5
* fix recovery of allocator ops after a growfs * Do not fail repairs on metadata files with no attr fork Signed-off-by: Carlos Maiolino <cem@kernel.org> -----BEGIN PGP SIGNATURE----- iJUEABMJAB0WIQQMHYkcUKcy4GgPe2RGdaER5QtfpgUCZxjo7gAKCRBGdaER5Qtf pr26AYCUc9+Vlg5iReesrghYHJgeCaMYZm2i4WdNdI+BO8d+5+AA1oUO55ib3xWd fX8A0MEBf32eeMR0E+K0NeKsmHnbGHXyWRg/27IlNRniL4/yldssEFB8X3b7Gkw5 /geUVdz99A== =+NGs -----END PGP SIGNATURE----- Merge tag 'xfs-6.12-fixes-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux Pull xfs fixes from Carlos Maiolino: - Fix recovery of allocator ops after a growfs - Do not fail repairs on metadata files with no attr fork * tag 'xfs-6.12-fixes-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: xfs: update the pag for the last AG at recovery time xfs: don't use __GFP_RETRY_MAYFAIL in xfs_initialize_perag xfs: error out when a superblock buffer update reduces the agcount xfs: update the file system geometry after recoverying superblock buffers xfs: merge the perag freeing helpers xfs: pass the exact range to initialize to xfs_initialize_perag xfs: don't fail repairs on metadata files with no attr fork
This commit is contained in:
commit
a8b3be2617
@ -185,17 +185,20 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free up the per-ag resources associated with the mount structure.
|
* Free up the per-ag resources within the specified AG range.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
xfs_free_perag(
|
xfs_free_perag_range(
|
||||||
struct xfs_mount *mp)
|
struct xfs_mount *mp,
|
||||||
|
xfs_agnumber_t first_agno,
|
||||||
|
xfs_agnumber_t end_agno)
|
||||||
|
|
||||||
{
|
{
|
||||||
struct xfs_perag *pag;
|
|
||||||
xfs_agnumber_t agno;
|
xfs_agnumber_t agno;
|
||||||
|
|
||||||
for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
|
for (agno = first_agno; agno < end_agno; agno++) {
|
||||||
pag = xa_erase(&mp->m_perags, agno);
|
struct xfs_perag *pag = xa_erase(&mp->m_perags, agno);
|
||||||
|
|
||||||
ASSERT(pag);
|
ASSERT(pag);
|
||||||
XFS_IS_CORRUPT(pag->pag_mount, atomic_read(&pag->pag_ref) != 0);
|
XFS_IS_CORRUPT(pag->pag_mount, atomic_read(&pag->pag_ref) != 0);
|
||||||
xfs_defer_drain_free(&pag->pag_intents_drain);
|
xfs_defer_drain_free(&pag->pag_intents_drain);
|
||||||
@ -270,54 +273,37 @@ xfs_agino_range(
|
|||||||
return __xfs_agino_range(mp, xfs_ag_block_count(mp, agno), first, last);
|
return __xfs_agino_range(mp, xfs_ag_block_count(mp, agno), first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int
|
||||||
* Free perag within the specified AG range, it is only used to free unused
|
xfs_update_last_ag_size(
|
||||||
* perags under the error handling path.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
xfs_free_unused_perag_range(
|
|
||||||
struct xfs_mount *mp,
|
struct xfs_mount *mp,
|
||||||
xfs_agnumber_t agstart,
|
xfs_agnumber_t prev_agcount)
|
||||||
xfs_agnumber_t agend)
|
|
||||||
{
|
{
|
||||||
struct xfs_perag *pag;
|
struct xfs_perag *pag = xfs_perag_grab(mp, prev_agcount - 1);
|
||||||
xfs_agnumber_t index;
|
|
||||||
|
|
||||||
for (index = agstart; index < agend; index++) {
|
if (!pag)
|
||||||
pag = xa_erase(&mp->m_perags, index);
|
return -EFSCORRUPTED;
|
||||||
if (!pag)
|
pag->block_count = __xfs_ag_block_count(mp, prev_agcount - 1,
|
||||||
break;
|
mp->m_sb.sb_agcount, mp->m_sb.sb_dblocks);
|
||||||
xfs_buf_cache_destroy(&pag->pag_bcache);
|
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
||||||
xfs_defer_drain_free(&pag->pag_intents_drain);
|
&pag->agino_max);
|
||||||
kfree(pag);
|
xfs_perag_rele(pag);
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
xfs_initialize_perag(
|
xfs_initialize_perag(
|
||||||
struct xfs_mount *mp,
|
struct xfs_mount *mp,
|
||||||
xfs_agnumber_t agcount,
|
xfs_agnumber_t old_agcount,
|
||||||
|
xfs_agnumber_t new_agcount,
|
||||||
xfs_rfsblock_t dblocks,
|
xfs_rfsblock_t dblocks,
|
||||||
xfs_agnumber_t *maxagi)
|
xfs_agnumber_t *maxagi)
|
||||||
{
|
{
|
||||||
struct xfs_perag *pag;
|
struct xfs_perag *pag;
|
||||||
xfs_agnumber_t index;
|
xfs_agnumber_t index;
|
||||||
xfs_agnumber_t first_initialised = NULLAGNUMBER;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
/*
|
for (index = old_agcount; index < new_agcount; index++) {
|
||||||
* Walk the current per-ag tree so we don't try to initialise AGs
|
pag = kzalloc(sizeof(*pag), GFP_KERNEL);
|
||||||
* that already exist (growfs case). Allocate and insert all the
|
|
||||||
* AGs we don't find ready for initialisation.
|
|
||||||
*/
|
|
||||||
for (index = 0; index < agcount; index++) {
|
|
||||||
pag = xfs_perag_get(mp, index);
|
|
||||||
if (pag) {
|
|
||||||
xfs_perag_put(pag);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
pag = kzalloc(sizeof(*pag), GFP_KERNEL | __GFP_RETRY_MAYFAIL);
|
|
||||||
if (!pag) {
|
if (!pag) {
|
||||||
error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
goto out_unwind_new_pags;
|
goto out_unwind_new_pags;
|
||||||
@ -353,21 +339,17 @@ xfs_initialize_perag(
|
|||||||
/* Active ref owned by mount indicates AG is online. */
|
/* Active ref owned by mount indicates AG is online. */
|
||||||
atomic_set(&pag->pag_active_ref, 1);
|
atomic_set(&pag->pag_active_ref, 1);
|
||||||
|
|
||||||
/* first new pag is fully initialized */
|
|
||||||
if (first_initialised == NULLAGNUMBER)
|
|
||||||
first_initialised = index;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pre-calculated geometry
|
* Pre-calculated geometry
|
||||||
*/
|
*/
|
||||||
pag->block_count = __xfs_ag_block_count(mp, index, agcount,
|
pag->block_count = __xfs_ag_block_count(mp, index, new_agcount,
|
||||||
dblocks);
|
dblocks);
|
||||||
pag->min_block = XFS_AGFL_BLOCK(mp);
|
pag->min_block = XFS_AGFL_BLOCK(mp);
|
||||||
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
|
||||||
&pag->agino_max);
|
&pag->agino_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
index = xfs_set_inode_alloc(mp, agcount);
|
index = xfs_set_inode_alloc(mp, new_agcount);
|
||||||
|
|
||||||
if (maxagi)
|
if (maxagi)
|
||||||
*maxagi = index;
|
*maxagi = index;
|
||||||
@ -381,8 +363,7 @@ out_remove_pag:
|
|||||||
out_free_pag:
|
out_free_pag:
|
||||||
kfree(pag);
|
kfree(pag);
|
||||||
out_unwind_new_pags:
|
out_unwind_new_pags:
|
||||||
/* unwind any prior newly initialized pags */
|
xfs_free_perag_range(mp, old_agcount, index);
|
||||||
xfs_free_unused_perag_range(mp, first_initialised, agcount);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,12 +144,13 @@ __XFS_AG_OPSTATE(prefers_metadata, PREFERS_METADATA)
|
|||||||
__XFS_AG_OPSTATE(allows_inodes, ALLOWS_INODES)
|
__XFS_AG_OPSTATE(allows_inodes, ALLOWS_INODES)
|
||||||
__XFS_AG_OPSTATE(agfl_needs_reset, AGFL_NEEDS_RESET)
|
__XFS_AG_OPSTATE(agfl_needs_reset, AGFL_NEEDS_RESET)
|
||||||
|
|
||||||
void xfs_free_unused_perag_range(struct xfs_mount *mp, xfs_agnumber_t agstart,
|
int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t old_agcount,
|
||||||
xfs_agnumber_t agend);
|
xfs_agnumber_t agcount, xfs_rfsblock_t dcount,
|
||||||
int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount,
|
xfs_agnumber_t *maxagi);
|
||||||
xfs_rfsblock_t dcount, xfs_agnumber_t *maxagi);
|
void xfs_free_perag_range(struct xfs_mount *mp, xfs_agnumber_t first_agno,
|
||||||
|
xfs_agnumber_t end_agno);
|
||||||
int xfs_initialize_perag_data(struct xfs_mount *mp, xfs_agnumber_t agno);
|
int xfs_initialize_perag_data(struct xfs_mount *mp, xfs_agnumber_t agno);
|
||||||
void xfs_free_perag(struct xfs_mount *mp);
|
int xfs_update_last_ag_size(struct xfs_mount *mp, xfs_agnumber_t prev_agcount);
|
||||||
|
|
||||||
/* Passive AG references */
|
/* Passive AG references */
|
||||||
struct xfs_perag *xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno);
|
struct xfs_perag *xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno);
|
||||||
|
@ -1084,9 +1084,11 @@ xrep_metadata_inode_forks(
|
|||||||
return error;
|
return error;
|
||||||
|
|
||||||
/* Make sure the attr fork looks ok before we delete it. */
|
/* Make sure the attr fork looks ok before we delete it. */
|
||||||
error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTA);
|
if (xfs_inode_hasattr(sc->ip)) {
|
||||||
if (error)
|
error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTA);
|
||||||
return error;
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear the reflink flag since metadata never shares. */
|
/* Clear the reflink flag since metadata never shares. */
|
||||||
if (xfs_is_reflink_inode(sc->ip)) {
|
if (xfs_is_reflink_inode(sc->ip)) {
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
#include "xfs_inode.h"
|
#include "xfs_inode.h"
|
||||||
#include "xfs_dir2.h"
|
#include "xfs_dir2.h"
|
||||||
#include "xfs_quota.h"
|
#include "xfs_quota.h"
|
||||||
|
#include "xfs_alloc.h"
|
||||||
|
#include "xfs_ag.h"
|
||||||
|
#include "xfs_sb.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the number of entries in the l_buf_cancel_table used during
|
* This is the number of entries in the l_buf_cancel_table used during
|
||||||
@ -684,6 +687,67 @@ xlog_recover_do_inode_buffer(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the in-memory superblock and perag structures from the primary SB
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* This is required because transactions running after growfs may require the
|
||||||
|
* updated values to be set in a previous fully commit transaction.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
xlog_recover_do_primary_sb_buffer(
|
||||||
|
struct xfs_mount *mp,
|
||||||
|
struct xlog_recover_item *item,
|
||||||
|
struct xfs_buf *bp,
|
||||||
|
struct xfs_buf_log_format *buf_f,
|
||||||
|
xfs_lsn_t current_lsn)
|
||||||
|
{
|
||||||
|
struct xfs_dsb *dsb = bp->b_addr;
|
||||||
|
xfs_agnumber_t orig_agcount = mp->m_sb.sb_agcount;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
|
||||||
|
|
||||||
|
if (orig_agcount == 0) {
|
||||||
|
xfs_alert(mp, "Trying to grow file system without AGs");
|
||||||
|
return -EFSCORRUPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the in-core super block from the freshly recovered on-disk one.
|
||||||
|
*/
|
||||||
|
xfs_sb_from_disk(&mp->m_sb, dsb);
|
||||||
|
|
||||||
|
if (mp->m_sb.sb_agcount < orig_agcount) {
|
||||||
|
xfs_alert(mp, "Shrinking AG count in log recovery not supported");
|
||||||
|
return -EFSCORRUPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Growfs can also grow the last existing AG. In this case we also need
|
||||||
|
* to update the length in the in-core perag structure and values
|
||||||
|
* depending on it.
|
||||||
|
*/
|
||||||
|
error = xfs_update_last_ag_size(mp, orig_agcount);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the new perags, and also update various block and inode
|
||||||
|
* allocator setting based off the number of AGs or total blocks.
|
||||||
|
* Because of the latter this also needs to happen if the agcount did
|
||||||
|
* not change.
|
||||||
|
*/
|
||||||
|
error = xfs_initialize_perag(mp, orig_agcount, mp->m_sb.sb_agcount,
|
||||||
|
mp->m_sb.sb_dblocks, &mp->m_maxagi);
|
||||||
|
if (error) {
|
||||||
|
xfs_warn(mp, "Failed recovery per-ag init: %d", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* V5 filesystems know the age of the buffer on disk being recovered. We can
|
* V5 filesystems know the age of the buffer on disk being recovered. We can
|
||||||
* have newer objects on disk than we are replaying, and so for these cases we
|
* have newer objects on disk than we are replaying, and so for these cases we
|
||||||
@ -967,6 +1031,12 @@ xlog_recover_buf_commit_pass2(
|
|||||||
dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
|
dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
|
||||||
if (!dirty)
|
if (!dirty)
|
||||||
goto out_release;
|
goto out_release;
|
||||||
|
} else if ((xfs_blft_from_flags(buf_f) & XFS_BLFT_SB_BUF) &&
|
||||||
|
xfs_buf_daddr(bp) == 0) {
|
||||||
|
error = xlog_recover_do_primary_sb_buffer(mp, item, bp, buf_f,
|
||||||
|
current_lsn);
|
||||||
|
if (error)
|
||||||
|
goto out_release;
|
||||||
} else {
|
} else {
|
||||||
xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
|
xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ xfs_growfs_data_private(
|
|||||||
struct xfs_mount *mp, /* mount point for filesystem */
|
struct xfs_mount *mp, /* mount point for filesystem */
|
||||||
struct xfs_growfs_data *in) /* growfs data input struct */
|
struct xfs_growfs_data *in) /* growfs data input struct */
|
||||||
{
|
{
|
||||||
|
xfs_agnumber_t oagcount = mp->m_sb.sb_agcount;
|
||||||
struct xfs_buf *bp;
|
struct xfs_buf *bp;
|
||||||
int error;
|
int error;
|
||||||
xfs_agnumber_t nagcount;
|
xfs_agnumber_t nagcount;
|
||||||
@ -94,7 +95,6 @@ xfs_growfs_data_private(
|
|||||||
xfs_rfsblock_t nb, nb_div, nb_mod;
|
xfs_rfsblock_t nb, nb_div, nb_mod;
|
||||||
int64_t delta;
|
int64_t delta;
|
||||||
bool lastag_extended = false;
|
bool lastag_extended = false;
|
||||||
xfs_agnumber_t oagcount;
|
|
||||||
struct xfs_trans *tp;
|
struct xfs_trans *tp;
|
||||||
struct aghdr_init_data id = {};
|
struct aghdr_init_data id = {};
|
||||||
struct xfs_perag *last_pag;
|
struct xfs_perag *last_pag;
|
||||||
@ -138,16 +138,14 @@ xfs_growfs_data_private(
|
|||||||
if (delta == 0)
|
if (delta == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
oagcount = mp->m_sb.sb_agcount;
|
/* TODO: shrinking the entire AGs hasn't yet completed */
|
||||||
/* allocate the new per-ag structures */
|
if (nagcount < oagcount)
|
||||||
if (nagcount > oagcount) {
|
|
||||||
error = xfs_initialize_perag(mp, nagcount, nb, &nagimax);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
} else if (nagcount < oagcount) {
|
|
||||||
/* TODO: shrinking the entire AGs hasn't yet completed */
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
/* allocate the new per-ag structures */
|
||||||
|
error = xfs_initialize_perag(mp, oagcount, nagcount, nb, &nagimax);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
if (delta > 0)
|
if (delta > 0)
|
||||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
|
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
|
||||||
@ -231,7 +229,7 @@ out_trans_cancel:
|
|||||||
xfs_trans_cancel(tp);
|
xfs_trans_cancel(tp);
|
||||||
out_free_unused_perag:
|
out_free_unused_perag:
|
||||||
if (nagcount > oagcount)
|
if (nagcount > oagcount)
|
||||||
xfs_free_unused_perag_range(mp, oagcount, nagcount);
|
xfs_free_perag_range(mp, oagcount, nagcount);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3393,13 +3393,6 @@ xlog_do_recover(
|
|||||||
/* re-initialise in-core superblock and geometry structures */
|
/* re-initialise in-core superblock and geometry structures */
|
||||||
mp->m_features |= xfs_sb_version_to_features(sbp);
|
mp->m_features |= xfs_sb_version_to_features(sbp);
|
||||||
xfs_reinit_percpu_counters(mp);
|
xfs_reinit_percpu_counters(mp);
|
||||||
error = xfs_initialize_perag(mp, sbp->sb_agcount, sbp->sb_dblocks,
|
|
||||||
&mp->m_maxagi);
|
|
||||||
if (error) {
|
|
||||||
xfs_warn(mp, "Failed post-recovery per-ag init: %d", error);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
|
|
||||||
|
|
||||||
/* Normal transactions can now occur */
|
/* Normal transactions can now occur */
|
||||||
clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
|
clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
|
||||||
|
@ -810,8 +810,8 @@ xfs_mountfs(
|
|||||||
/*
|
/*
|
||||||
* Allocate and initialize the per-ag data.
|
* Allocate and initialize the per-ag data.
|
||||||
*/
|
*/
|
||||||
error = xfs_initialize_perag(mp, sbp->sb_agcount, mp->m_sb.sb_dblocks,
|
error = xfs_initialize_perag(mp, 0, sbp->sb_agcount,
|
||||||
&mp->m_maxagi);
|
mp->m_sb.sb_dblocks, &mp->m_maxagi);
|
||||||
if (error) {
|
if (error) {
|
||||||
xfs_warn(mp, "Failed per-ag init: %d", error);
|
xfs_warn(mp, "Failed per-ag init: %d", error);
|
||||||
goto out_free_dir;
|
goto out_free_dir;
|
||||||
@ -1048,7 +1048,7 @@ xfs_mountfs(
|
|||||||
xfs_buftarg_drain(mp->m_logdev_targp);
|
xfs_buftarg_drain(mp->m_logdev_targp);
|
||||||
xfs_buftarg_drain(mp->m_ddev_targp);
|
xfs_buftarg_drain(mp->m_ddev_targp);
|
||||||
out_free_perag:
|
out_free_perag:
|
||||||
xfs_free_perag(mp);
|
xfs_free_perag_range(mp, 0, mp->m_sb.sb_agcount);
|
||||||
out_free_dir:
|
out_free_dir:
|
||||||
xfs_da_unmount(mp);
|
xfs_da_unmount(mp);
|
||||||
out_remove_uuid:
|
out_remove_uuid:
|
||||||
@ -1129,8 +1129,7 @@ xfs_unmountfs(
|
|||||||
xfs_errortag_clearall(mp);
|
xfs_errortag_clearall(mp);
|
||||||
#endif
|
#endif
|
||||||
shrinker_free(mp->m_inodegc_shrinker);
|
shrinker_free(mp->m_inodegc_shrinker);
|
||||||
xfs_free_perag(mp);
|
xfs_free_perag_range(mp, 0, mp->m_sb.sb_agcount);
|
||||||
|
|
||||||
xfs_errortag_del(mp);
|
xfs_errortag_del(mp);
|
||||||
xfs_error_sysfs_del(mp);
|
xfs_error_sysfs_del(mp);
|
||||||
xchk_stats_unregister(mp->m_scrub_stats);
|
xchk_stats_unregister(mp->m_scrub_stats);
|
||||||
|
Loading…
Reference in New Issue
Block a user