Commit a8b3be26 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 850925a8 4a201dcf
Loading
Loading
Loading
Loading
+28 −47
Original line number Diff line number Diff line
@@ -185,17 +185,20 @@ xfs_initialize_perag_data(
}

/*
 * Free up the per-ag resources associated with the mount structure.
 * Free up the per-ag resources  within the specified AG range.
 */
void
xfs_free_perag(
	struct xfs_mount	*mp)
xfs_free_perag_range(
	struct xfs_mount	*mp,
	xfs_agnumber_t		first_agno,
	xfs_agnumber_t		end_agno)

{
	struct xfs_perag	*pag;
	xfs_agnumber_t		agno;

	for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
		pag = xa_erase(&mp->m_perags, agno);
	for (agno = first_agno; agno < end_agno; agno++) {
		struct xfs_perag	*pag = xa_erase(&mp->m_perags, agno);

		ASSERT(pag);
		XFS_IS_CORRUPT(pag->pag_mount, atomic_read(&pag->pag_ref) != 0);
		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);
}

/*
 * Free perag within the specified AG range, it is only used to free unused
 * perags under the error handling path.
 */
void
xfs_free_unused_perag_range(
int
xfs_update_last_ag_size(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agstart,
	xfs_agnumber_t		agend)
	xfs_agnumber_t		prev_agcount)
{
	struct xfs_perag	*pag;
	xfs_agnumber_t		index;
	struct xfs_perag	*pag = xfs_perag_grab(mp, prev_agcount - 1);

	for (index = agstart; index < agend; index++) {
		pag = xa_erase(&mp->m_perags, index);
	if (!pag)
			break;
		xfs_buf_cache_destroy(&pag->pag_bcache);
		xfs_defer_drain_free(&pag->pag_intents_drain);
		kfree(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;
}

int
xfs_initialize_perag(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agcount,
	xfs_agnumber_t		old_agcount,
	xfs_agnumber_t		new_agcount,
	xfs_rfsblock_t		dblocks,
	xfs_agnumber_t		*maxagi)
{
	struct xfs_perag	*pag;
	xfs_agnumber_t		index;
	xfs_agnumber_t		first_initialised = NULLAGNUMBER;
	int			error;

	/*
	 * Walk the current per-ag tree so we don't try to initialise AGs
	 * 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);
	for (index = old_agcount; index < new_agcount; index++) {
		pag = kzalloc(sizeof(*pag), GFP_KERNEL);
		if (!pag) {
			error = -ENOMEM;
			goto out_unwind_new_pags;
@@ -353,21 +339,17 @@ xfs_initialize_perag(
		/* Active ref owned by mount indicates AG is online. */
		atomic_set(&pag->pag_active_ref, 1);

		/* first new pag is fully initialized */
		if (first_initialised == NULLAGNUMBER)
			first_initialised = index;

		/*
		 * 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);
		pag->min_block = XFS_AGFL_BLOCK(mp);
		__xfs_agino_range(mp, pag->block_count, &pag->agino_min,
				&pag->agino_max);
	}

	index = xfs_set_inode_alloc(mp, agcount);
	index = xfs_set_inode_alloc(mp, new_agcount);

	if (maxagi)
		*maxagi = index;
@@ -381,8 +363,7 @@ xfs_initialize_perag(
out_free_pag:
	kfree(pag);
out_unwind_new_pags:
	/* unwind any prior newly initialized pags */
	xfs_free_unused_perag_range(mp, first_initialised, agcount);
	xfs_free_perag_range(mp, old_agcount, index);
	return error;
}

+6 −5
Original line number Diff line number Diff line
@@ -144,12 +144,13 @@ __XFS_AG_OPSTATE(prefers_metadata, PREFERS_METADATA)
__XFS_AG_OPSTATE(allows_inodes, ALLOWS_INODES)
__XFS_AG_OPSTATE(agfl_needs_reset, AGFL_NEEDS_RESET)

void xfs_free_unused_perag_range(struct xfs_mount *mp, xfs_agnumber_t agstart,
			xfs_agnumber_t agend);
int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount,
			xfs_rfsblock_t dcount, xfs_agnumber_t *maxagi);
int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t old_agcount,
		xfs_agnumber_t agcount, 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);
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 */
struct xfs_perag *xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno);
+5 −3
Original line number Diff line number Diff line
@@ -1084,9 +1084,11 @@ xrep_metadata_inode_forks(
		return error;

	/* Make sure the attr fork looks ok before we delete it. */
	if (xfs_inode_hasattr(sc->ip)) {
		error = xrep_metadata_inode_subtype(sc, XFS_SCRUB_TYPE_BMBTA);
		if (error)
			return error;
	}

	/* Clear the reflink flag since metadata never shares. */
	if (xfs_is_reflink_inode(sc->ip)) {
+70 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@
#include "xfs_inode.h"
#include "xfs_dir2.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
@@ -684,6 +687,67 @@ xlog_recover_do_inode_buffer(
	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
 * 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);
		if (!dirty)
			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 {
		xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
	}
+9 −11
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ xfs_growfs_data_private(
	struct xfs_mount	*mp,		/* mount point for filesystem */
	struct xfs_growfs_data	*in)		/* growfs data input struct */
{
	xfs_agnumber_t		oagcount = mp->m_sb.sb_agcount;
	struct xfs_buf		*bp;
	int			error;
	xfs_agnumber_t		nagcount;
@@ -94,7 +95,6 @@ xfs_growfs_data_private(
	xfs_rfsblock_t		nb, nb_div, nb_mod;
	int64_t			delta;
	bool			lastag_extended = false;
	xfs_agnumber_t		oagcount;
	struct xfs_trans	*tp;
	struct aghdr_init_data	id = {};
	struct xfs_perag	*last_pag;
@@ -138,16 +138,14 @@ xfs_growfs_data_private(
	if (delta == 0)
		return 0;

	oagcount = mp->m_sb.sb_agcount;
	/* TODO: shrinking the entire AGs hasn't yet completed */
	if (nagcount < oagcount)
		return -EINVAL;

	/* allocate the new per-ag structures */
	if (nagcount > oagcount) {
		error = xfs_initialize_perag(mp, nagcount, nb, &nagimax);
	error = xfs_initialize_perag(mp, oagcount, nagcount, nb, &nagimax);
	if (error)
		return error;
	} else if (nagcount < oagcount) {
		/* TODO: shrinking the entire AGs hasn't yet completed */
		return -EINVAL;
	}

	if (delta > 0)
		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_growdata,
@@ -231,7 +229,7 @@ xfs_growfs_data_private(
	xfs_trans_cancel(tp);
out_free_unused_perag:
	if (nagcount > oagcount)
		xfs_free_unused_perag_range(mp, oagcount, nagcount);
		xfs_free_perag_range(mp, oagcount, nagcount);
	return error;
}

Loading