Commit 9f7b640f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-5.14-merge-6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs updates from Darrick Wong:
 "Most of the work this cycle has been on refactoring various parts of
  the codebase. The biggest non-cleanup changes are (1) reducing the
  number of cache flushes sent when writing the log; (2) a substantial
  number of log recovery fixes; and (3) I started accepting pull
  requests from contributors if the commits in their branches match
  what's been sent to the list.

  For a week or so I /had/ staged a major cleanup of the logging code
  from Dave Chinner, but it exposed so many lurking bugs in other parts
  of the logging and log recovery code that I decided to defer that
  patchset until we can address those latent bugs.

  Larger cleanups this time include walking the incore inode cache (me)
  and rework of the extended attribute code (Allison) to prepare it for
  adding logged xattr updates (and directory tree parent pointers) in
  future releases.

  Summary:

   - Refactor the buffer cache to use bulk page allocation

   - Convert agnumber-based AG iteration to walk per-AG structures

   - Clean up some unit conversions and other code warts

   - Reduce spinlock contention in the directio fastpath

   - Collapse all the inode cache walks into a single function

   - Remove indirect function calls from the inode cache walk code

   - Dramatically reduce the number of cache flushes sent when writing
     log buffers

   - Preserve inode sickness reports for longer

   - Rename xfs_eofblocks since it controls inode cache walks

   - Refactor the extended attribute code to prepare it for the addition
     of log intent items to make xattrs fully transactional

   - A few fixes to earlier large patchsets

   - Log recovery fixes so that we don't accidentally mark the log clean
     when log intent recovery fails

   - Fix some latent SOB errors

   - Clean up shutdown messages that get logged to dmesg

   - Fix a regression in the online shrink code

   - Fix a UAF in the buffer logging code if the fs goes offline

   - Fix uninitialized error variables

   - Fix a UAF in the CIL when commited log item callbacks race with a
     shutdown

   - Fix a bug where the CIL could hang trying to push part of the log
     ring buffer that hasn't been filled yet"

* tag 'xfs-5.14-merge-6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (102 commits)
  xfs: don't wait on future iclogs when pushing the CIL
  xfs: Fix a CIL UAF by getting get rid of the iclog callback lock
  xfs: remove callback dequeue loop from xlog_state_do_iclog_callbacks
  xfs: don't nest icloglock inside ic_callback_lock
  xfs: Initialize error in xfs_attr_remove_iter
  xfs: fix endianness issue in xfs_ag_shrink_space
  xfs: remove dead stale buf unpin handling code
  xfs: hold buffer across unpin and potential shutdown processing
  xfs: force the log offline when log intent item recovery fails
  xfs: fix log intent recovery ENOSPC shutdowns when inactivating inodes
  xfs: shorten the shutdown messages to a single line
  xfs: print name of function causing fs shutdown instead of hex pointer
  xfs: fix type mismatches in the inode reclaim functions
  xfs: separate primary inode selection criteria in xfs_iget_cache_hit
  xfs: refactor the inode recycling code
  xfs: add iclog state trace events
  xfs: xfs_log_force_lsn isn't passed a LSN
  xfs: Fix CIL throttle hang when CIL space used going backwards
  xfs: journal IO cache flush reductions
  xfs: remove need_start_rec parameter from xlog_write()
  ...
parents e4aa67b0 1effb72a
Loading
Loading
Loading
Loading
+275 −5
Original line number Diff line number Diff line
@@ -27,6 +27,276 @@
#include "xfs_defer.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_trace.h"
#include "xfs_inode.h"
#include "xfs_icache.h"


/*
 * Passive reference counting access wrappers to the perag structures.  If the
 * per-ag structure is to be freed, the freeing code is responsible for cleaning
 * up objects with passive references before freeing the structure. This is
 * things like cached buffers.
 */
struct xfs_perag *
xfs_perag_get(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agno)
{
	struct xfs_perag	*pag;
	int			ref = 0;

	rcu_read_lock();
	pag = radix_tree_lookup(&mp->m_perag_tree, agno);
	if (pag) {
		ASSERT(atomic_read(&pag->pag_ref) >= 0);
		ref = atomic_inc_return(&pag->pag_ref);
	}
	rcu_read_unlock();
	trace_xfs_perag_get(mp, agno, ref, _RET_IP_);
	return pag;
}

/*
 * search from @first to find the next perag with the given tag set.
 */
struct xfs_perag *
xfs_perag_get_tag(
	struct xfs_mount	*mp,
	xfs_agnumber_t		first,
	unsigned int		tag)
{
	struct xfs_perag	*pag;
	int			found;
	int			ref;

	rcu_read_lock();
	found = radix_tree_gang_lookup_tag(&mp->m_perag_tree,
					(void **)&pag, first, 1, tag);
	if (found <= 0) {
		rcu_read_unlock();
		return NULL;
	}
	ref = atomic_inc_return(&pag->pag_ref);
	rcu_read_unlock();
	trace_xfs_perag_get_tag(mp, pag->pag_agno, ref, _RET_IP_);
	return pag;
}

void
xfs_perag_put(
	struct xfs_perag	*pag)
{
	int	ref;

	ASSERT(atomic_read(&pag->pag_ref) > 0);
	ref = atomic_dec_return(&pag->pag_ref);
	trace_xfs_perag_put(pag->pag_mount, pag->pag_agno, ref, _RET_IP_);
}

/*
 * 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(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agcount)
{
	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;

	for (index = 0; index < agcount; index++) {
		/*
		 * read the agf, then the agi. This gets us
		 * all the information we need and populates the
		 * per-ag structures for us.
		 */
		error = xfs_alloc_pagf_init(mp, NULL, index, 0);
		if (error)
			return error;

		error = xfs_ialloc_pagi_init(mp, NULL, index);
		if (error)
			return error;
		pag = xfs_perag_get(mp, index);
		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.");
		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;
}

STATIC void
__xfs_free_perag(
	struct rcu_head	*head)
{
	struct xfs_perag *pag = container_of(head, struct xfs_perag, rcu_head);

	ASSERT(!delayed_work_pending(&pag->pag_blockgc_work));
	ASSERT(atomic_read(&pag->pag_ref) == 0);
	kmem_free(pag);
}

/*
 * Free up the per-ag resources associated with the mount structure.
 */
void
xfs_free_perag(
	struct xfs_mount	*mp)
{
	struct xfs_perag	*pag;
	xfs_agnumber_t		agno;

	for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
		spin_lock(&mp->m_perag_lock);
		pag = radix_tree_delete(&mp->m_perag_tree, agno);
		spin_unlock(&mp->m_perag_lock);
		ASSERT(pag);
		ASSERT(atomic_read(&pag->pag_ref) == 0);

		cancel_delayed_work_sync(&pag->pag_blockgc_work);
		xfs_iunlink_destroy(pag);
		xfs_buf_hash_destroy(pag);

		call_rcu(&pag->rcu_head, __xfs_free_perag);
	}
}

int
xfs_initialize_perag(
	struct xfs_mount	*mp,
	xfs_agnumber_t		agcount,
	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 = kmem_zalloc(sizeof(*pag), KM_MAYFAIL);
		if (!pag) {
			error = -ENOMEM;
			goto out_unwind_new_pags;
		}
		pag->pag_agno = index;
		pag->pag_mount = mp;

		error = radix_tree_preload(GFP_NOFS);
		if (error)
			goto out_free_pag;

		spin_lock(&mp->m_perag_lock);
		if (radix_tree_insert(&mp->m_perag_tree, index, pag)) {
			WARN_ON_ONCE(1);
			spin_unlock(&mp->m_perag_lock);
			radix_tree_preload_end();
			error = -EEXIST;
			goto out_free_pag;
		}
		spin_unlock(&mp->m_perag_lock);
		radix_tree_preload_end();

		/* Place kernel structure only init below this point. */
		spin_lock_init(&pag->pag_ici_lock);
		spin_lock_init(&pag->pagb_lock);
		spin_lock_init(&pag->pag_state_lock);
		INIT_DELAYED_WORK(&pag->pag_blockgc_work, xfs_blockgc_worker);
		INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC);
		init_waitqueue_head(&pag->pagb_wait);
		pag->pagb_count = 0;
		pag->pagb_tree = RB_ROOT;

		error = xfs_buf_hash_init(pag);
		if (error)
			goto out_remove_pag;

		error = xfs_iunlink_init(pag);
		if (error)
			goto out_hash_destroy;

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

	index = xfs_set_inode_alloc(mp, agcount);

	if (maxagi)
		*maxagi = index;

	mp->m_ag_prealloc_blocks = xfs_prealloc_blocks(mp);
	return 0;

out_hash_destroy:
	xfs_buf_hash_destroy(pag);
out_remove_pag:
	radix_tree_delete(&mp->m_perag_tree, index);
out_free_pag:
	kmem_free(pag);
out_unwind_new_pags:
	/* unwind any prior newly initialized pags */
	for (index = first_initialised; index < agcount; index++) {
		pag = radix_tree_delete(&mp->m_perag_tree, index);
		if (!pag)
			break;
		xfs_buf_hash_destroy(pag);
		xfs_iunlink_destroy(pag);
		kmem_free(pag);
	}
	return error;
}

static int
xfs_get_aghdr_buf(
@@ -43,7 +313,6 @@ xfs_get_aghdr_buf(
	if (error)
		return error;

	xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
	bp->b_bn = blkno;
	bp->b_maps[0].bm_bn = blkno;
	bp->b_ops = ops;
@@ -510,6 +779,7 @@ xfs_ag_shrink_space(
	struct xfs_buf		*agibp, *agfbp;
	struct xfs_agi		*agi;
	struct xfs_agf		*agf;
	xfs_agblock_t		aglen;
	int			error, err2;

	ASSERT(agno == mp->m_sb.sb_agcount - 1);
@@ -524,14 +794,14 @@ xfs_ag_shrink_space(
		return error;

	agf = agfbp->b_addr;
	aglen = be32_to_cpu(agi->agi_length);
	/* some extra paranoid checks before we shrink the ag */
	if (XFS_IS_CORRUPT(mp, agf->agf_length != agi->agi_length))
		return -EFSCORRUPTED;
	if (delta >= agi->agi_length)
	if (delta >= aglen)
		return -EINVAL;

	args.fsbno = XFS_AGB_TO_FSB(mp, agno,
				    be32_to_cpu(agi->agi_length) - delta);
	args.fsbno = XFS_AGB_TO_FSB(mp, agno, aglen - delta);

	/*
	 * Disable perag reservations so it doesn't cause the allocation request
@@ -646,7 +916,7 @@ xfs_ag_extend_space(
	 * XFS_RMAP_OINFO_SKIP_UPDATE is used here to tell the rmap btree that
	 * this doesn't actually exist in the rmap btree.
	 */
	error = xfs_rmap_free(tp, bp, id->agno,
	error = xfs_rmap_free(tp, bp, bp->b_pag,
				be32_to_cpu(agf->agf_length) - len,
				len, &XFS_RMAP_OINFO_SKIP_UPDATE);
	if (error)
+136 −0
Original line number Diff line number Diff line
@@ -9,6 +9,142 @@

struct xfs_mount;
struct xfs_trans;
struct xfs_perag;

/*
 * Per-ag infrastructure
 */

/* per-AG block reservation data structures*/
struct xfs_ag_resv {
	/* number of blocks originally reserved here */
	xfs_extlen_t			ar_orig_reserved;
	/* number of blocks reserved here */
	xfs_extlen_t			ar_reserved;
	/* number of blocks originally asked for */
	xfs_extlen_t			ar_asked;
};

/*
 * Per-ag incore structure, copies of information in agf and agi, to improve the
 * performance of allocation group selection.
 */
struct xfs_perag {
	struct xfs_mount *pag_mount;	/* owner filesystem */
	xfs_agnumber_t	pag_agno;	/* AG this structure belongs to */
	atomic_t	pag_ref;	/* perag reference count */
	char		pagf_init;	/* this agf's entry is initialized */
	char		pagi_init;	/* this agi's entry is initialized */
	char		pagf_metadata;	/* the agf is preferred to be metadata */
	char		pagi_inodeok;	/* The agi is ok for inodes */
	uint8_t		pagf_levels[XFS_BTNUM_AGF];
					/* # of levels in bno & cnt btree */
	bool		pagf_agflreset; /* agfl requires reset before use */
	uint32_t	pagf_flcount;	/* count of blocks in freelist */
	xfs_extlen_t	pagf_freeblks;	/* total free blocks */
	xfs_extlen_t	pagf_longest;	/* longest free space */
	uint32_t	pagf_btreeblks;	/* # of blocks held in AGF btrees */
	xfs_agino_t	pagi_freecount;	/* number of free inodes */
	xfs_agino_t	pagi_count;	/* number of allocated inodes */

	/*
	 * Inode allocation search lookup optimisation.
	 * If the pagino matches, the search for new inodes
	 * doesn't need to search the near ones again straight away
	 */
	xfs_agino_t	pagl_pagino;
	xfs_agino_t	pagl_leftrec;
	xfs_agino_t	pagl_rightrec;

	int		pagb_count;	/* pagb slots in use */
	uint8_t		pagf_refcount_level; /* recount btree height */

	/* Blocks reserved for all kinds of metadata. */
	struct xfs_ag_resv	pag_meta_resv;
	/* Blocks reserved for the reverse mapping btree. */
	struct xfs_ag_resv	pag_rmapbt_resv;

	/* -- kernel only structures below this line -- */

	/*
	 * Bitsets of per-ag metadata that have been checked and/or are sick.
	 * Callers should hold pag_state_lock before accessing this field.
	 */
	uint16_t	pag_checked;
	uint16_t	pag_sick;
	spinlock_t	pag_state_lock;

	spinlock_t	pagb_lock;	/* lock for pagb_tree */
	struct rb_root	pagb_tree;	/* ordered tree of busy extents */
	unsigned int	pagb_gen;	/* generation count for pagb_tree */
	wait_queue_head_t pagb_wait;	/* woken when pagb_gen changes */

	atomic_t        pagf_fstrms;    /* # of filestreams active in this AG */

	spinlock_t	pag_ici_lock;	/* incore inode cache lock */
	struct radix_tree_root pag_ici_root;	/* incore inode cache root */
	int		pag_ici_reclaimable;	/* reclaimable inodes */
	unsigned long	pag_ici_reclaim_cursor;	/* reclaim restart point */

	/* buffer cache index */
	spinlock_t	pag_buf_lock;	/* lock for pag_buf_hash */
	struct rhashtable pag_buf_hash;

	/* for rcu-safe freeing */
	struct rcu_head	rcu_head;

	/* background prealloc block trimming */
	struct delayed_work	pag_blockgc_work;

	/*
	 * Unlinked inode information.  This incore information reflects
	 * data stored in the AGI, so callers must hold the AGI buffer lock
	 * or have some other means to control concurrency.
	 */
	struct rhashtable	pagi_unlinked_hash;
};

int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount,
			xfs_agnumber_t *maxagi);
int xfs_initialize_perag_data(struct xfs_mount *mp, xfs_agnumber_t agno);
void xfs_free_perag(struct xfs_mount *mp);

struct xfs_perag *xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno);
struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *mp, xfs_agnumber_t agno,
		unsigned int tag);
void xfs_perag_put(struct xfs_perag *pag);

/*
 * Perag iteration APIs
 *
 * XXX: for_each_perag_range() usage really needs an iterator to clean up when
 * we terminate at end_agno because we may have taken a reference to the perag
 * beyond end_agno. Right now callers have to be careful to catch and clean that
 * up themselves. This is not necessary for the callers of for_each_perag() and
 * for_each_perag_from() because they terminate at sb_agcount where there are
 * no perag structures in tree beyond end_agno.
 */
#define for_each_perag_range(mp, next_agno, end_agno, pag) \
	for ((pag) = xfs_perag_get((mp), (next_agno)); \
		(pag) != NULL && (next_agno) <= (end_agno); \
		(next_agno) = (pag)->pag_agno + 1, \
		xfs_perag_put(pag), \
		(pag) = xfs_perag_get((mp), (next_agno)))

#define for_each_perag_from(mp, next_agno, pag) \
	for_each_perag_range((mp), (next_agno), (mp)->m_sb.sb_agcount, (pag))


#define for_each_perag(mp, agno, pag) \
	(agno) = 0; \
	for_each_perag_from((mp), (agno), (pag))

#define for_each_perag_tag(mp, agno, pag, tag) \
	for ((agno) = 0, (pag) = xfs_perag_get_tag((mp), 0, (tag)); \
		(pag) != NULL; \
		(agno) = (pag)->pag_agno + 1, \
		xfs_perag_put(pag), \
		(pag) = xfs_perag_get_tag((mp), (agno), (tag)))

struct aghdr_init_data {
	/* per ag data */
+5 −6
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@
#include "xfs_btree.h"
#include "xfs_refcount_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_ag_resv.h"

/*
@@ -250,7 +250,6 @@ xfs_ag_resv_init(
	struct xfs_trans		*tp)
{
	struct xfs_mount		*mp = pag->pag_mount;
	xfs_agnumber_t			agno = pag->pag_agno;
	xfs_extlen_t			ask;
	xfs_extlen_t			used;
	int				error = 0, error2;
@@ -260,11 +259,11 @@ xfs_ag_resv_init(
	if (pag->pag_meta_resv.ar_asked == 0) {
		ask = used = 0;

		error = xfs_refcountbt_calc_reserves(mp, tp, agno, &ask, &used);
		error = xfs_refcountbt_calc_reserves(mp, tp, pag, &ask, &used);
		if (error)
			goto out;

		error = xfs_finobt_calc_reserves(mp, tp, agno, &ask, &used);
		error = xfs_finobt_calc_reserves(mp, tp, pag, &ask, &used);
		if (error)
			goto out;

@@ -282,7 +281,7 @@ xfs_ag_resv_init(

			mp->m_finobt_nores = true;

			error = xfs_refcountbt_calc_reserves(mp, tp, agno, &ask,
			error = xfs_refcountbt_calc_reserves(mp, tp, pag, &ask,
					&used);
			if (error)
				goto out;
@@ -300,7 +299,7 @@ xfs_ag_resv_init(
	if (pag->pag_rmapbt_resv.ar_asked == 0) {
		ask = used = 0;

		error = xfs_rmapbt_calc_reserves(mp, tp, agno, &ask, &used);
		error = xfs_rmapbt_calc_reserves(mp, tp, pag, &ask, &used);
		if (error)
			goto out;

+15 −0
Original line number Diff line number Diff line
@@ -18,6 +18,21 @@ void xfs_ag_resv_alloc_extent(struct xfs_perag *pag, enum xfs_ag_resv_type type,
void xfs_ag_resv_free_extent(struct xfs_perag *pag, enum xfs_ag_resv_type type,
		struct xfs_trans *tp, xfs_extlen_t len);

static inline struct xfs_ag_resv *
xfs_perag_resv(
	struct xfs_perag	*pag,
	enum xfs_ag_resv_type	type)
{
	switch (type) {
	case XFS_AG_RESV_METADATA:
		return &pag->pag_meta_resv;
	case XFS_AG_RESV_RMAPBT:
		return &pag->pag_rmapbt_resv;
	default:
		return NULL;
	}
}

/*
 * RMAPBT reservation accounting wrappers. Since rmapbt blocks are sourced from
 * the AGFL, they are allocated one at a time and the reservation updates don't
+56 −55
Original line number Diff line number Diff line
@@ -10,7 +10,6 @@
#include "xfs_shared.h"
#include "xfs_trans_resv.h"
#include "xfs_bit.h"
#include "xfs_sb.h"
#include "xfs_mount.h"
#include "xfs_defer.h"
#include "xfs_btree.h"
@@ -24,6 +23,7 @@
#include "xfs_trans.h"
#include "xfs_buf_item.h"
#include "xfs_log.h"
#include "xfs_ag.h"
#include "xfs_ag_resv.h"
#include "xfs_bmap.h"

@@ -230,7 +230,7 @@ xfs_alloc_get_rec(
	int			*stat)	/* output: success/failure */
{
	struct xfs_mount	*mp = cur->bc_mp;
	xfs_agnumber_t		agno = cur->bc_ag.agno;
	xfs_agnumber_t		agno = cur->bc_ag.pag->pag_agno;
	union xfs_btree_rec	*rec;
	int			error;

@@ -776,7 +776,7 @@ xfs_alloc_cur_setup(
	 */
	if (!acur->cnt)
		acur->cnt = xfs_allocbt_init_cursor(args->mp, args->tp,
					args->agbp, args->agno, XFS_BTNUM_CNT);
					args->agbp, args->pag, XFS_BTNUM_CNT);
	error = xfs_alloc_lookup_ge(acur->cnt, 0, args->maxlen, &i);
	if (error)
		return error;
@@ -786,10 +786,10 @@ xfs_alloc_cur_setup(
	 */
	if (!acur->bnolt)
		acur->bnolt = xfs_allocbt_init_cursor(args->mp, args->tp,
					args->agbp, args->agno, XFS_BTNUM_BNO);
					args->agbp, args->pag, XFS_BTNUM_BNO);
	if (!acur->bnogt)
		acur->bnogt = xfs_allocbt_init_cursor(args->mp, args->tp,
					args->agbp, args->agno, XFS_BTNUM_BNO);
					args->agbp, args->pag, XFS_BTNUM_BNO);
	return i == 1 ? 0 : -ENOSPC;
}

@@ -1063,7 +1063,7 @@ xfs_alloc_ag_vextent_small(
	if (fbno == NULLAGBLOCK)
		goto out;

	xfs_extent_busy_reuse(args->mp, args->agno, fbno, 1,
	xfs_extent_busy_reuse(args->mp, args->pag, fbno, 1,
			      (args->datatype & XFS_ALLOC_NOBUSY));

	if (args->datatype & XFS_ALLOC_USERDATA) {
@@ -1089,7 +1089,7 @@ xfs_alloc_ag_vextent_small(
	 * If we're feeding an AGFL block to something that doesn't live in the
	 * free space, we need to clear out the OWN_AG rmap.
	 */
	error = xfs_rmap_free(args->tp, args->agbp, args->agno, fbno, 1,
	error = xfs_rmap_free(args->tp, args->agbp, args->pag, fbno, 1,
			      &XFS_RMAP_OINFO_AG);
	if (error)
		goto error;
@@ -1166,7 +1166,7 @@ xfs_alloc_ag_vextent(

	/* if not file data, insert new block into the reverse map btree */
	if (!xfs_rmap_should_skip_owner_update(&args->oinfo)) {
		error = xfs_rmap_alloc(args->tp, args->agbp, args->agno,
		error = xfs_rmap_alloc(args->tp, args->agbp, args->pag,
				       args->agbno, args->len, &args->oinfo);
		if (error)
			return error;
@@ -1178,7 +1178,7 @@ xfs_alloc_ag_vextent(
		if (error)
			return error;

		ASSERT(!xfs_extent_busy_search(args->mp, args->agno,
		ASSERT(!xfs_extent_busy_search(args->mp, args->pag,
					      args->agbno, args->len));
	}

@@ -1217,7 +1217,7 @@ xfs_alloc_ag_vextent_exact(
	 * Allocate/initialize a cursor for the by-number freespace btree.
	 */
	bno_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
					  args->agno, XFS_BTNUM_BNO);
					  args->pag, XFS_BTNUM_BNO);

	/*
	 * Lookup bno and minlen in the btree (minlen is irrelevant, really).
@@ -1277,7 +1277,7 @@ xfs_alloc_ag_vextent_exact(
	 * Allocate/initialize a cursor for the by-size btree.
	 */
	cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
		args->agno, XFS_BTNUM_CNT);
					args->pag, XFS_BTNUM_CNT);
	ASSERT(args->agbno + args->len <= be32_to_cpu(agf->agf_length));
	error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen, args->agbno,
				      args->len, XFSA_FIXUP_BNO_OK);
@@ -1674,9 +1674,8 @@ xfs_alloc_ag_vextent_size(
	 * Allocate and initialize a cursor for the by-size btree.
	 */
	cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
		args->agno, XFS_BTNUM_CNT);
					args->pag, XFS_BTNUM_CNT);
	bno_cur = NULL;
	busy = false;

	/*
	 * Look for an entry >= maxlen+alignment-1 blocks.
@@ -1837,7 +1836,7 @@ xfs_alloc_ag_vextent_size(
	 * Allocate and initialize a cursor for the by-block tree.
	 */
	bno_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
		args->agno, XFS_BTNUM_BNO);
					args->pag, XFS_BTNUM_BNO);
	if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen,
			rbno, rlen, XFSA_FIXUP_CNT_OK)))
		goto error0;
@@ -1896,12 +1895,13 @@ xfs_free_ag_extent(
	int				haveright; /* have a right neighbor */
	int				i;
	int				error;
	struct xfs_perag		*pag = agbp->b_pag;

	bno_cur = cnt_cur = NULL;
	mp = tp->t_mountp;

	if (!xfs_rmap_should_skip_owner_update(oinfo)) {
		error = xfs_rmap_free(tp, agbp, agno, bno, len, oinfo);
		error = xfs_rmap_free(tp, agbp, pag, bno, len, oinfo);
		if (error)
			goto error0;
	}
@@ -1909,7 +1909,7 @@ xfs_free_ag_extent(
	/*
	 * Allocate and initialize a cursor for the by-block btree.
	 */
	bno_cur = xfs_allocbt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_BNO);
	bno_cur = xfs_allocbt_init_cursor(mp, tp, agbp, pag, XFS_BTNUM_BNO);
	/*
	 * Look for a neighboring block on the left (lower block numbers)
	 * that is contiguous with this space.
@@ -1979,7 +1979,7 @@ xfs_free_ag_extent(
	/*
	 * Now allocate and initialize a cursor for the by-size tree.
	 */
	cnt_cur = xfs_allocbt_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_CNT);
	cnt_cur = xfs_allocbt_init_cursor(mp, tp, agbp, pag, XFS_BTNUM_CNT);
	/*
	 * Have both left and right contiguous neighbors.
	 * Merge all three into a single free block.
@@ -2490,7 +2490,7 @@ xfs_exact_minlen_extent_available(
	int			error = 0;

	cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, agbp,
			args->agno, XFS_BTNUM_CNT);
					args->pag, XFS_BTNUM_CNT);
	error = xfs_alloc_lookup_ge(cnt_cur, 0, args->minlen, stat);
	if (error)
		goto out;
@@ -2693,21 +2693,21 @@ xfs_alloc_fix_freelist(
 * Get a block from the freelist.
 * Returns with the buffer for the block gotten.
 */
int				/* error */
int
xfs_alloc_get_freelist(
	xfs_trans_t	*tp,	/* transaction pointer */
	struct xfs_buf	*agbp,	/* buffer containing the agf structure */
	xfs_agblock_t	*bnop,	/* block address retrieved from freelist */
	int		btreeblk) /* destination is a AGF btree */
	struct xfs_trans	*tp,
	struct xfs_buf		*agbp,
	xfs_agblock_t		*bnop,
	int			btreeblk)
{
	struct xfs_agf		*agf = agbp->b_addr;
	struct xfs_buf	*agflbp;/* buffer for a.g. freelist structure */
	xfs_agblock_t	bno;	/* block number returned */
	struct xfs_buf		*agflbp;
	xfs_agblock_t		bno;
	__be32			*agfl_bno;
	int			error;
	int			logflags;
	xfs_mount_t	*mp = tp->t_mountp;
	xfs_perag_t	*pag;	/* per allocation group data */
	struct xfs_mount	*mp = tp->t_mountp;
	struct xfs_perag	*pag;

	/*
	 * Freelist is empty, give up.
@@ -2817,20 +2817,20 @@ xfs_alloc_pagf_init(
/*
 * Put the block on the freelist for the allocation group.
 */
int					/* error */
int
xfs_alloc_put_freelist(
	xfs_trans_t		*tp,	/* transaction pointer */
	struct xfs_buf		*agbp,	/* buffer for a.g. freelist header */
	struct xfs_buf		*agflbp,/* buffer for a.g. free block array */
	xfs_agblock_t		bno,	/* block being freed */
	int			btreeblk) /* block came from a AGF btree */
	struct xfs_trans	*tp,
	struct xfs_buf		*agbp,
	struct xfs_buf		*agflbp,
	xfs_agblock_t		bno,
	int			btreeblk)
{
	struct xfs_mount	*mp = tp->t_mountp;
	struct xfs_agf		*agf = agbp->b_addr;
	__be32			*blockp;/* pointer to array entry */
	struct xfs_perag	*pag;
	__be32			*blockp;
	int			error;
	int			logflags;
	xfs_perag_t		*pag;	/* per allocation group data */
	__be32			*agfl_bno;
	int			startoff;

@@ -3292,7 +3292,7 @@ xfs_alloc_vextent(
int
xfs_free_extent_fix_freelist(
	struct xfs_trans	*tp,
	xfs_agnumber_t		agno,
	struct xfs_perag	*pag,
	struct xfs_buf		**agbp)
{
	struct xfs_alloc_arg	args;
@@ -3301,7 +3301,8 @@ xfs_free_extent_fix_freelist(
	memset(&args, 0, sizeof(struct xfs_alloc_arg));
	args.tp = tp;
	args.mp = tp->t_mountp;
	args.agno = agno;
	args.agno = pag->pag_agno;
	args.pag = pag;

	/*
	 * validate that the block number is legal - the enables us to detect
@@ -3310,17 +3311,12 @@ xfs_free_extent_fix_freelist(
	if (args.agno >= args.mp->m_sb.sb_agcount)
		return -EFSCORRUPTED;

	args.pag = xfs_perag_get(args.mp, args.agno);
	ASSERT(args.pag);

	error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
	if (error)
		goto out;
		return error;

	*agbp = args.agbp;
out:
	xfs_perag_put(args.pag);
	return error;
	return 0;
}

/*
@@ -3344,6 +3340,7 @@ __xfs_free_extent(
	struct xfs_agf			*agf;
	int				error;
	unsigned int			busy_flags = 0;
	struct xfs_perag		*pag;

	ASSERT(len != 0);
	ASSERT(type != XFS_AG_RESV_AGFL);
@@ -3352,33 +3349,37 @@ __xfs_free_extent(
			XFS_ERRTAG_FREE_EXTENT))
		return -EIO;

	error = xfs_free_extent_fix_freelist(tp, agno, &agbp);
	pag = xfs_perag_get(mp, agno);
	error = xfs_free_extent_fix_freelist(tp, pag, &agbp);
	if (error)
		return error;
		goto err;
	agf = agbp->b_addr;

	if (XFS_IS_CORRUPT(mp, agbno >= mp->m_sb.sb_agblocks)) {
		error = -EFSCORRUPTED;
		goto err;
		goto err_release;
	}

	/* validate the extent size is legal now we have the agf locked */
	if (XFS_IS_CORRUPT(mp, agbno + len > be32_to_cpu(agf->agf_length))) {
		error = -EFSCORRUPTED;
		goto err;
		goto err_release;
	}

	error = xfs_free_ag_extent(tp, agbp, agno, agbno, len, oinfo, type);
	if (error)
		goto err;
		goto err_release;

	if (skip_discard)
		busy_flags |= XFS_EXTENT_BUSY_SKIP_DISCARD;
	xfs_extent_busy_insert(tp, agno, agbno, len, busy_flags);
	xfs_extent_busy_insert(tp, pag, agbno, len, busy_flags);
	xfs_perag_put(pag);
	return 0;

err:
err_release:
	xfs_trans_brelse(tp, agbp);
err:
	xfs_perag_put(pag);
	return error;
}

Loading