diff mbox series

[5/8] xfs: refactor inode unlinked pointer update functions

Message ID 154897670150.26065.2878806054016276963.stgit@magnolia (mailing list archive)
State Superseded
Headers show
Series xfs: incore unlinked list | expand

Commit Message

Darrick J. Wong Jan. 31, 2019, 11:18 p.m. UTC
From: Darrick J. Wong <darrick.wong@oracle.com>

Hoist the functions that update an inode's unlinked pointer updates into
a helper.  No functional changes.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 fs/xfs/xfs_inode.c |  188 +++++++++++++++++++++++++++-------------------------
 fs/xfs/xfs_trace.h |   26 +++++++
 2 files changed, 124 insertions(+), 90 deletions(-)

Comments

Brian Foster Feb. 1, 2019, 7:01 p.m. UTC | #1
On Thu, Jan 31, 2019 at 03:18:21PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Hoist the functions that update an inode's unlinked pointer updates into
> a helper.  No functional changes.
> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  fs/xfs/xfs_inode.c |  188 +++++++++++++++++++++++++++-------------------------
>  fs/xfs/xfs_trace.h |   26 +++++++
>  2 files changed, 124 insertions(+), 90 deletions(-)
> 
> 
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index 4ddda3f3255f..ea38b66fbc59 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -1920,6 +1920,88 @@ xfs_iunlink_update_bucket(
>  	return 0;
>  }
>  
> +/* Set an on-disk inode's unlinked pointer and return the old value. */

Doesn't look like this one returns anything.

> +STATIC void
> +xfs_iunlink_update_dinode(
> +	struct xfs_trans	*tp,
> +	struct xfs_buf		*ibp,
> +	struct xfs_dinode	*dip,
> +	struct xfs_imap		*imap,
> +	xfs_ino_t		ino,
> +	xfs_agino_t		new_agino)
> +{
> +	struct xfs_mount	*mp = tp->t_mountp;
> +	int			offset;
> +
> +	ASSERT(new_agino == NULLAGINO ||
> +	       xfs_verify_agino(mp, XFS_INO_TO_AGNO(mp, ino), new_agino));
> +
> +	trace_xfs_iunlink_update_dinode(mp,
> +			XFS_INO_TO_AGNO(mp, ino),
> +			XFS_INO_TO_AGINO(mp, ino),
> +			be32_to_cpu(dip->di_next_unlinked), new_agino);
> +
> +	dip->di_next_unlinked = cpu_to_be32(new_agino);
> +	offset = imap->im_boffset +
> +			offsetof(struct xfs_dinode, di_next_unlinked);
> +
> +	/* need to recalc the inode CRC if appropriate */
> +	xfs_dinode_calc_crc(mp, dip);
> +	xfs_trans_inode_buf(tp, ibp);
> +	xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1));
> +	xfs_inobp_check(mp, ibp);
> +}
> +
> +/* Set an in-core inode's unlinked pointer and return the old value. */
> +STATIC int
> +xfs_iunlink_update_inode(
> +	struct xfs_trans	*tp,
> +	struct xfs_inode	*ip,
> +	xfs_agino_t		new_agino,
> +	xfs_agino_t		*old_agino)
> +{
> +	struct xfs_mount	*mp = tp->t_mountp;
> +	struct xfs_dinode	*dip;
> +	struct xfs_buf		*ibp;
> +	xfs_agnumber_t		agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
> +	xfs_agino_t		old_value;
> +	int			error;
> +
> +	ASSERT(new_agino == NULLAGINO || xfs_verify_agino(mp, agno, new_agino));
> +
> +	error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp, 0, 0);
> +	if (error)
> +		return error;
> +
> +	/* Make sure the old pointer isn't garbage. */
> +	old_value = be32_to_cpu(dip->di_next_unlinked);
> +	if (old_value != NULLAGINO && !xfs_verify_agino(mp, agno, old_value)) {
> +		error = -EFSCORRUPTED;
> +		goto out;
> +	}
> +	*old_agino = old_value;
> +
> +	/*
> +	 * Since we're updating a linked list, we should never find that the
> +	 * current pointer is the same as the new value, unless we're
> +	 * terminating the list.
> +	 */
> +	*old_agino = old_value;

Double assign of *old_agino.

> +	if (old_value == new_agino) {
> +		if (new_agino != NULLAGINO)
> +			error = -EFSCORRUPTED;
> +		goto out;
> +	}
> +
> +	/* Ok, update the new pointer. */
> +	xfs_iunlink_update_dinode(tp, ibp, dip, &ip->i_imap, ip->i_ino,
> +			new_agino);
> +	return 0;
> +out:
> +	xfs_trans_brelse(tp, ibp);
> +	return error;
> +}
> +
>  /*
>   * This is called when the inode's link count goes to 0 or we are creating a
>   * tmpfile via O_TMPFILE. In the case of a tmpfile, @ignore_linkcount will be
> @@ -1937,15 +2019,12 @@ xfs_iunlink(
>  {
>  	struct xfs_mount	*mp = tp->t_mountp;
>  	struct xfs_agi		*agi;
> -	struct xfs_dinode	*dip;
>  	struct xfs_buf		*agibp;
> -	struct xfs_buf		*ibp;
>  	struct xfs_perag	*pag;
>  	xfs_agnumber_t		agno;
>  	xfs_agino_t		next_agino;
>  	xfs_agino_t		agino;
>  	short			bucket_index;
> -	int			offset;
>  	int			error;
>  
>  	ASSERT(VFS_I(ip)->i_mode != 0);
> @@ -1980,29 +2059,17 @@ xfs_iunlink(
>  	}
>  
>  	if (next_agino != NULLAGINO) {
> +		xfs_agino_t	old_agino;
> +
>  		/*
>  		 * There is already another inode in the bucket we need
>  		 * to add ourselves to.  Add us at the front of the list.
> -		 * Here we put the head pointer into our next pointer,
> -		 * and then we fall through to point the head at us.
>  		 */
> -		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
> -				       0, 0);
> +		error = xfs_iunlink_update_inode(tp, ip, next_agino,
> +				&old_agino);

What's left of the comment above seems a bit misleading wrt to what
we're doing here. Could we say something like "point this inode to the
current head of the list" instead of "add us to the front of the list?"

>  		if (error)
>  			goto out_unlock;
> -
> -		ASSERT(dip->di_next_unlinked == cpu_to_be32(NULLAGINO));
> -		dip->di_next_unlinked = agi->agi_unlinked[bucket_index];
> -		offset = ip->i_imap.im_boffset +
> -			offsetof(xfs_dinode_t, di_next_unlinked);
> -
> -		/* need to recalc the inode CRC if appropriate */
> -		xfs_dinode_calc_crc(mp, dip);
> -
> -		xfs_trans_inode_buf(tp, ibp);
> -		xfs_trans_log_buf(tp, ibp, offset,
> -				  (offset + sizeof(xfs_agino_t) - 1));
> -		xfs_inobp_check(mp, ibp);
> +		ASSERT(old_agino == NULLAGINO);
>  	}
>  
>  	/* Point the head of the list to point to this inode. */
> @@ -2027,9 +2094,7 @@ xfs_iunlink_remove(
>  {
>  	struct xfs_mount	*mp = tp->t_mountp;
>  	struct xfs_agi		*agi;
> -	struct xfs_dinode	*dip;
>  	struct xfs_buf		*agibp;
> -	struct xfs_buf		*ibp;
>  	struct xfs_buf		*last_ibp;
>  	struct xfs_dinode	*last_dip = NULL;
>  	struct xfs_perag	*pag;
> @@ -2038,8 +2103,6 @@ xfs_iunlink_remove(
>  	xfs_agino_t		agino;
>  	xfs_agino_t		next_agino;
>  	short			bucket_index;
> -	int			offset;
> -	int			last_offset = 0;
>  	int			error;
>  
>  	if (!xfs_verify_ino(mp, ip->i_ino))
> @@ -2076,34 +2139,11 @@ xfs_iunlink_remove(
>  		/*
>  		 * We're at the head of the list.  Get the inode's on-disk
>  		 * buffer to see if there is anyone after us on the list.
> -		 * Only modify our next pointer if it is not already NULLAGINO.
> -		 * This saves us the overhead of dealing with the buffer when
> -		 * there is no need to change it.
>  		 */
> -		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
> -				       0, 0);
> -		if (error) {
> -			xfs_warn(mp, "%s: xfs_imap_to_bp returned error %d.",
> -				__func__, error);
> +		error = xfs_iunlink_update_inode(tp, ip, NULLAGINO,
> +				&next_agino);
> +		if (error)
>  			goto out_unlock;
> -		}
> -		next_agino = be32_to_cpu(dip->di_next_unlinked);
> -		ASSERT(next_agino != 0);
> -		if (next_agino != NULLAGINO) {
> -			dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
> -			offset = ip->i_imap.im_boffset +
> -				offsetof(xfs_dinode_t, di_next_unlinked);
> -
> -			/* need to recalc the inode CRC if appropriate */
> -			xfs_dinode_calc_crc(mp, dip);
> -
> -			xfs_trans_inode_buf(tp, ibp);
> -			xfs_trans_log_buf(tp, ibp, offset,
> -					  (offset + sizeof(xfs_agino_t) - 1));
> -			xfs_inobp_check(mp, ibp);
> -		} else {
> -			xfs_trans_brelse(tp, ibp);
> -		}
>  
>  		/* Point the head of the list to the next unlinked inode. */
>  		error = xfs_iunlink_update_bucket(tp, agibp, bucket_index,
> @@ -2111,13 +2151,13 @@ xfs_iunlink_remove(
>  		if (error)
>  			goto out_unlock;
>  	} else {
> +		struct xfs_imap	imap;
> +
>  		/*
>  		 * We need to search the list for the inode being freed.
>  		 */
>  		last_ibp = NULL;
>  		while (next_agino != agino) {
> -			struct xfs_imap	imap;
> -
>  			if (last_ibp)
>  				xfs_trans_brelse(tp, last_ibp);
>  
> @@ -2141,7 +2181,6 @@ xfs_iunlink_remove(
>  				goto out_unlock;
>  			}
>  
> -			last_offset = imap.im_boffset;
>  			next_agino = be32_to_cpu(last_dip->di_next_unlinked);
>  			if (!xfs_verify_agino(mp, agno, next_agino)) {
>  				XFS_CORRUPTION_ERROR(__func__,
> @@ -2156,45 +2195,14 @@ xfs_iunlink_remove(
>  		 * Now last_ibp points to the buffer previous to us on the
>  		 * unlinked list.  Pull us from the list.
>  		 */
> -		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
> -				       0, 0);
> -		if (error) {
> -			xfs_warn(mp, "%s: xfs_imap_to_bp(2) returned error %d.",
> -				__func__, error);
> +		error = xfs_iunlink_update_inode(tp, ip, NULLAGINO,
> +				&next_agino);
> +		if (error)
>  			goto out_unlock;

It looks like the above _update_inode() call is now common across both
branches. We might be able to factor that out a bit further so the
if/else just determines whether we update the bucket or a previous
dinode.

Brian

> -		}
> -		next_agino = be32_to_cpu(dip->di_next_unlinked);
> -		ASSERT(next_agino != 0);
> -		ASSERT(next_agino != agino);
> -		if (next_agino != NULLAGINO) {
> -			dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
> -			offset = ip->i_imap.im_boffset +
> -				offsetof(xfs_dinode_t, di_next_unlinked);
> -
> -			/* need to recalc the inode CRC if appropriate */
> -			xfs_dinode_calc_crc(mp, dip);
> -
> -			xfs_trans_inode_buf(tp, ibp);
> -			xfs_trans_log_buf(tp, ibp, offset,
> -					  (offset + sizeof(xfs_agino_t) - 1));
> -			xfs_inobp_check(mp, ibp);
> -		} else {
> -			xfs_trans_brelse(tp, ibp);
> -		}
> -		/*
> -		 * Point the previous inode on the list to the next inode.
> -		 */
> -		last_dip->di_next_unlinked = cpu_to_be32(next_agino);
> -		ASSERT(next_agino != 0);
> -		offset = last_offset + offsetof(xfs_dinode_t, di_next_unlinked);
> -
> -		/* need to recalc the inode CRC if appropriate */
> -		xfs_dinode_calc_crc(mp, last_dip);
>  
> -		xfs_trans_inode_buf(tp, last_ibp);
> -		xfs_trans_log_buf(tp, last_ibp, offset,
> -				  (offset + sizeof(xfs_agino_t) - 1));
> -		xfs_inobp_check(mp, last_ibp);
> +		/* Point the previous inode on the list to the next inode. */
> +		xfs_iunlink_update_dinode(tp, last_ibp, last_dip, &imap,
> +				next_ino, next_agino);
>  	}
>  	pag->pagi_unlinked_count--;
>  
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index c10478e7e49a..fbec8f0e1a9a 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -3397,6 +3397,32 @@ TRACE_EVENT(xfs_iunlink_update_bucket,
>  		  __entry->new_ptr)
>  );
>  
> +TRACE_EVENT(xfs_iunlink_update_dinode,
> +	TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino,
> +		 xfs_agino_t old_ptr, xfs_agino_t new_ptr),
> +	TP_ARGS(mp, agno, agino, old_ptr, new_ptr),
> +	TP_STRUCT__entry(
> +		__field(dev_t, dev)
> +		__field(xfs_agnumber_t, agno)
> +		__field(xfs_agino_t, agino)
> +		__field(xfs_agino_t, old_ptr)
> +		__field(xfs_agino_t, new_ptr)
> +	),
> +	TP_fast_assign(
> +		__entry->dev = mp->m_super->s_dev;
> +		__entry->agno = agno;
> +		__entry->agino = agino;
> +		__entry->old_ptr = old_ptr;
> +		__entry->new_ptr = new_ptr;
> +	),
> +	TP_printk("dev %d:%d agno %u agino 0x%x old 0x%x new 0x%x",
> +		  MAJOR(__entry->dev), MINOR(__entry->dev),
> +		  __entry->agno,
> +		  __entry->agino,
> +		  __entry->old_ptr,
> +		  __entry->new_ptr)
> +);
> +
>  #endif /* _TRACE_XFS_H */
>  
>  #undef TRACE_INCLUDE_PATH
>
Christoph Hellwig Feb. 2, 2019, 4:27 p.m. UTC | #2
> +	ASSERT(new_agino == NULLAGINO ||
> +	       xfs_verify_agino(mp, XFS_INO_TO_AGNO(mp, ino), new_agino));

We have quite a few uses of this pattern.  Shouldn't we just add a
a xfs_verify_agino_or_null helper?

Also given that the caller has the agno and we use it a few times
maybe pass it as an argument?

Also shouldn't new_agino be called something like next_agino or at
least have a little comment explaining what it stands for?

> +	xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1));

No need for the innter braces.

Otherwise this looks conceptually fine to me.
Darrick J. Wong Feb. 2, 2019, 8:29 p.m. UTC | #3
On Sat, Feb 02, 2019 at 08:27:37AM -0800, Christoph Hellwig wrote:
> > +	ASSERT(new_agino == NULLAGINO ||
> > +	       xfs_verify_agino(mp, XFS_INO_TO_AGNO(mp, ino), new_agino));
> 
> We have quite a few uses of this pattern.  Shouldn't we just add a
> a xfs_verify_agino_or_null helper?

Yeah, I'll clean that up.

> Also given that the caller has the agno and we use it a few times
> maybe pass it as an argument?
> 
> Also shouldn't new_agino be called something like next_agino or at
> least have a little comment explaining what it stands for?
> 
> > +	xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1));
> 
> No need for the innter braces.
> 
> Otherwise this looks conceptually fine to me.

Ok, will fix this and the others.

--D
Darrick J. Wong Feb. 2, 2019, 10 p.m. UTC | #4
On Fri, Feb 01, 2019 at 02:01:17PM -0500, Brian Foster wrote:
> On Thu, Jan 31, 2019 at 03:18:21PM -0800, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Hoist the functions that update an inode's unlinked pointer updates into
> > a helper.  No functional changes.
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  fs/xfs/xfs_inode.c |  188 +++++++++++++++++++++++++++-------------------------
> >  fs/xfs/xfs_trace.h |   26 +++++++
> >  2 files changed, 124 insertions(+), 90 deletions(-)
> > 
> > 
> > diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> > index 4ddda3f3255f..ea38b66fbc59 100644
> > --- a/fs/xfs/xfs_inode.c
> > +++ b/fs/xfs/xfs_inode.c
> > @@ -1920,6 +1920,88 @@ xfs_iunlink_update_bucket(
> >  	return 0;
> >  }
> >  
> > +/* Set an on-disk inode's unlinked pointer and return the old value. */
> 
> Doesn't look like this one returns anything.

Heh, oops, will fix.

> > +STATIC void
> > +xfs_iunlink_update_dinode(
> > +	struct xfs_trans	*tp,
> > +	struct xfs_buf		*ibp,
> > +	struct xfs_dinode	*dip,
> > +	struct xfs_imap		*imap,
> > +	xfs_ino_t		ino,
> > +	xfs_agino_t		new_agino)
> > +{
> > +	struct xfs_mount	*mp = tp->t_mountp;
> > +	int			offset;
> > +
> > +	ASSERT(new_agino == NULLAGINO ||
> > +	       xfs_verify_agino(mp, XFS_INO_TO_AGNO(mp, ino), new_agino));
> > +
> > +	trace_xfs_iunlink_update_dinode(mp,
> > +			XFS_INO_TO_AGNO(mp, ino),
> > +			XFS_INO_TO_AGINO(mp, ino),
> > +			be32_to_cpu(dip->di_next_unlinked), new_agino);
> > +
> > +	dip->di_next_unlinked = cpu_to_be32(new_agino);
> > +	offset = imap->im_boffset +
> > +			offsetof(struct xfs_dinode, di_next_unlinked);
> > +
> > +	/* need to recalc the inode CRC if appropriate */
> > +	xfs_dinode_calc_crc(mp, dip);
> > +	xfs_trans_inode_buf(tp, ibp);
> > +	xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1));
> > +	xfs_inobp_check(mp, ibp);
> > +}
> > +
> > +/* Set an in-core inode's unlinked pointer and return the old value. */
> > +STATIC int
> > +xfs_iunlink_update_inode(
> > +	struct xfs_trans	*tp,
> > +	struct xfs_inode	*ip,
> > +	xfs_agino_t		new_agino,
> > +	xfs_agino_t		*old_agino)
> > +{
> > +	struct xfs_mount	*mp = tp->t_mountp;
> > +	struct xfs_dinode	*dip;
> > +	struct xfs_buf		*ibp;
> > +	xfs_agnumber_t		agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
> > +	xfs_agino_t		old_value;
> > +	int			error;
> > +
> > +	ASSERT(new_agino == NULLAGINO || xfs_verify_agino(mp, agno, new_agino));
> > +
> > +	error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp, 0, 0);
> > +	if (error)
> > +		return error;
> > +
> > +	/* Make sure the old pointer isn't garbage. */
> > +	old_value = be32_to_cpu(dip->di_next_unlinked);
> > +	if (old_value != NULLAGINO && !xfs_verify_agino(mp, agno, old_value)) {
> > +		error = -EFSCORRUPTED;
> > +		goto out;
> > +	}
> > +	*old_agino = old_value;
> > +
> > +	/*
> > +	 * Since we're updating a linked list, we should never find that the
> > +	 * current pointer is the same as the new value, unless we're
> > +	 * terminating the list.
> > +	 */
> > +	*old_agino = old_value;
> 
> Double assign of *old_agino.

Will fix.

> > +	if (old_value == new_agino) {
> > +		if (new_agino != NULLAGINO)
> > +			error = -EFSCORRUPTED;
> > +		goto out;
> > +	}
> > +
> > +	/* Ok, update the new pointer. */
> > +	xfs_iunlink_update_dinode(tp, ibp, dip, &ip->i_imap, ip->i_ino,
> > +			new_agino);
> > +	return 0;
> > +out:
> > +	xfs_trans_brelse(tp, ibp);
> > +	return error;
> > +}
> > +
> >  /*
> >   * This is called when the inode's link count goes to 0 or we are creating a
> >   * tmpfile via O_TMPFILE. In the case of a tmpfile, @ignore_linkcount will be
> > @@ -1937,15 +2019,12 @@ xfs_iunlink(
> >  {
> >  	struct xfs_mount	*mp = tp->t_mountp;
> >  	struct xfs_agi		*agi;
> > -	struct xfs_dinode	*dip;
> >  	struct xfs_buf		*agibp;
> > -	struct xfs_buf		*ibp;
> >  	struct xfs_perag	*pag;
> >  	xfs_agnumber_t		agno;
> >  	xfs_agino_t		next_agino;
> >  	xfs_agino_t		agino;
> >  	short			bucket_index;
> > -	int			offset;
> >  	int			error;
> >  
> >  	ASSERT(VFS_I(ip)->i_mode != 0);
> > @@ -1980,29 +2059,17 @@ xfs_iunlink(
> >  	}
> >  
> >  	if (next_agino != NULLAGINO) {
> > +		xfs_agino_t	old_agino;
> > +
> >  		/*
> >  		 * There is already another inode in the bucket we need
> >  		 * to add ourselves to.  Add us at the front of the list.
> > -		 * Here we put the head pointer into our next pointer,
> > -		 * and then we fall through to point the head at us.
> >  		 */
> > -		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
> > -				       0, 0);
> > +		error = xfs_iunlink_update_inode(tp, ip, next_agino,
> > +				&old_agino);
> 
> What's left of the comment above seems a bit misleading wrt to what
> we're doing here. Could we say something like "point this inode to the
> current head of the list" instead of "add us to the front of the list?"

Ok.

> >  		if (error)
> >  			goto out_unlock;
> > -
> > -		ASSERT(dip->di_next_unlinked == cpu_to_be32(NULLAGINO));
> > -		dip->di_next_unlinked = agi->agi_unlinked[bucket_index];
> > -		offset = ip->i_imap.im_boffset +
> > -			offsetof(xfs_dinode_t, di_next_unlinked);
> > -
> > -		/* need to recalc the inode CRC if appropriate */
> > -		xfs_dinode_calc_crc(mp, dip);
> > -
> > -		xfs_trans_inode_buf(tp, ibp);
> > -		xfs_trans_log_buf(tp, ibp, offset,
> > -				  (offset + sizeof(xfs_agino_t) - 1));
> > -		xfs_inobp_check(mp, ibp);
> > +		ASSERT(old_agino == NULLAGINO);
> >  	}
> >  
> >  	/* Point the head of the list to point to this inode. */
> > @@ -2027,9 +2094,7 @@ xfs_iunlink_remove(
> >  {
> >  	struct xfs_mount	*mp = tp->t_mountp;
> >  	struct xfs_agi		*agi;
> > -	struct xfs_dinode	*dip;
> >  	struct xfs_buf		*agibp;
> > -	struct xfs_buf		*ibp;
> >  	struct xfs_buf		*last_ibp;
> >  	struct xfs_dinode	*last_dip = NULL;
> >  	struct xfs_perag	*pag;
> > @@ -2038,8 +2103,6 @@ xfs_iunlink_remove(
> >  	xfs_agino_t		agino;
> >  	xfs_agino_t		next_agino;
> >  	short			bucket_index;
> > -	int			offset;
> > -	int			last_offset = 0;
> >  	int			error;
> >  
> >  	if (!xfs_verify_ino(mp, ip->i_ino))
> > @@ -2076,34 +2139,11 @@ xfs_iunlink_remove(
> >  		/*
> >  		 * We're at the head of the list.  Get the inode's on-disk
> >  		 * buffer to see if there is anyone after us on the list.
> > -		 * Only modify our next pointer if it is not already NULLAGINO.
> > -		 * This saves us the overhead of dealing with the buffer when
> > -		 * there is no need to change it.
> >  		 */
> > -		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
> > -				       0, 0);
> > -		if (error) {
> > -			xfs_warn(mp, "%s: xfs_imap_to_bp returned error %d.",
> > -				__func__, error);
> > +		error = xfs_iunlink_update_inode(tp, ip, NULLAGINO,
> > +				&next_agino);
> > +		if (error)
> >  			goto out_unlock;
> > -		}
> > -		next_agino = be32_to_cpu(dip->di_next_unlinked);
> > -		ASSERT(next_agino != 0);
> > -		if (next_agino != NULLAGINO) {
> > -			dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
> > -			offset = ip->i_imap.im_boffset +
> > -				offsetof(xfs_dinode_t, di_next_unlinked);
> > -
> > -			/* need to recalc the inode CRC if appropriate */
> > -			xfs_dinode_calc_crc(mp, dip);
> > -
> > -			xfs_trans_inode_buf(tp, ibp);
> > -			xfs_trans_log_buf(tp, ibp, offset,
> > -					  (offset + sizeof(xfs_agino_t) - 1));
> > -			xfs_inobp_check(mp, ibp);
> > -		} else {
> > -			xfs_trans_brelse(tp, ibp);
> > -		}
> >  
> >  		/* Point the head of the list to the next unlinked inode. */
> >  		error = xfs_iunlink_update_bucket(tp, agibp, bucket_index,
> > @@ -2111,13 +2151,13 @@ xfs_iunlink_remove(
> >  		if (error)
> >  			goto out_unlock;
> >  	} else {
> > +		struct xfs_imap	imap;
> > +
> >  		/*
> >  		 * We need to search the list for the inode being freed.
> >  		 */
> >  		last_ibp = NULL;
> >  		while (next_agino != agino) {
> > -			struct xfs_imap	imap;
> > -
> >  			if (last_ibp)
> >  				xfs_trans_brelse(tp, last_ibp);
> >  
> > @@ -2141,7 +2181,6 @@ xfs_iunlink_remove(
> >  				goto out_unlock;
> >  			}
> >  
> > -			last_offset = imap.im_boffset;
> >  			next_agino = be32_to_cpu(last_dip->di_next_unlinked);
> >  			if (!xfs_verify_agino(mp, agno, next_agino)) {
> >  				XFS_CORRUPTION_ERROR(__func__,
> > @@ -2156,45 +2195,14 @@ xfs_iunlink_remove(
> >  		 * Now last_ibp points to the buffer previous to us on the
> >  		 * unlinked list.  Pull us from the list.
> >  		 */
> > -		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
> > -				       0, 0);
> > -		if (error) {
> > -			xfs_warn(mp, "%s: xfs_imap_to_bp(2) returned error %d.",
> > -				__func__, error);
> > +		error = xfs_iunlink_update_inode(tp, ip, NULLAGINO,
> > +				&next_agino);
> > +		if (error)
> >  			goto out_unlock;
> 
> It looks like the above _update_inode() call is now common across both
> branches. We might be able to factor that out a bit further so the
> if/else just determines whether we update the bucket or a previous
> dinode.

I'll do that, though I think I'll do that as a separate patch.

--D

> Brian
> 
> > -		}
> > -		next_agino = be32_to_cpu(dip->di_next_unlinked);
> > -		ASSERT(next_agino != 0);
> > -		ASSERT(next_agino != agino);
> > -		if (next_agino != NULLAGINO) {
> > -			dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
> > -			offset = ip->i_imap.im_boffset +
> > -				offsetof(xfs_dinode_t, di_next_unlinked);
> > -
> > -			/* need to recalc the inode CRC if appropriate */
> > -			xfs_dinode_calc_crc(mp, dip);
> > -
> > -			xfs_trans_inode_buf(tp, ibp);
> > -			xfs_trans_log_buf(tp, ibp, offset,
> > -					  (offset + sizeof(xfs_agino_t) - 1));
> > -			xfs_inobp_check(mp, ibp);
> > -		} else {
> > -			xfs_trans_brelse(tp, ibp);
> > -		}
> > -		/*
> > -		 * Point the previous inode on the list to the next inode.
> > -		 */
> > -		last_dip->di_next_unlinked = cpu_to_be32(next_agino);
> > -		ASSERT(next_agino != 0);
> > -		offset = last_offset + offsetof(xfs_dinode_t, di_next_unlinked);
> > -
> > -		/* need to recalc the inode CRC if appropriate */
> > -		xfs_dinode_calc_crc(mp, last_dip);
> >  
> > -		xfs_trans_inode_buf(tp, last_ibp);
> > -		xfs_trans_log_buf(tp, last_ibp, offset,
> > -				  (offset + sizeof(xfs_agino_t) - 1));
> > -		xfs_inobp_check(mp, last_ibp);
> > +		/* Point the previous inode on the list to the next inode. */
> > +		xfs_iunlink_update_dinode(tp, last_ibp, last_dip, &imap,
> > +				next_ino, next_agino);
> >  	}
> >  	pag->pagi_unlinked_count--;
> >  
> > diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> > index c10478e7e49a..fbec8f0e1a9a 100644
> > --- a/fs/xfs/xfs_trace.h
> > +++ b/fs/xfs/xfs_trace.h
> > @@ -3397,6 +3397,32 @@ TRACE_EVENT(xfs_iunlink_update_bucket,
> >  		  __entry->new_ptr)
> >  );
> >  
> > +TRACE_EVENT(xfs_iunlink_update_dinode,
> > +	TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino,
> > +		 xfs_agino_t old_ptr, xfs_agino_t new_ptr),
> > +	TP_ARGS(mp, agno, agino, old_ptr, new_ptr),
> > +	TP_STRUCT__entry(
> > +		__field(dev_t, dev)
> > +		__field(xfs_agnumber_t, agno)
> > +		__field(xfs_agino_t, agino)
> > +		__field(xfs_agino_t, old_ptr)
> > +		__field(xfs_agino_t, new_ptr)
> > +	),
> > +	TP_fast_assign(
> > +		__entry->dev = mp->m_super->s_dev;
> > +		__entry->agno = agno;
> > +		__entry->agino = agino;
> > +		__entry->old_ptr = old_ptr;
> > +		__entry->new_ptr = new_ptr;
> > +	),
> > +	TP_printk("dev %d:%d agno %u agino 0x%x old 0x%x new 0x%x",
> > +		  MAJOR(__entry->dev), MINOR(__entry->dev),
> > +		  __entry->agno,
> > +		  __entry->agino,
> > +		  __entry->old_ptr,
> > +		  __entry->new_ptr)
> > +);
> > +
> >  #endif /* _TRACE_XFS_H */
> >  
> >  #undef TRACE_INCLUDE_PATH
> >
diff mbox series

Patch

diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 4ddda3f3255f..ea38b66fbc59 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1920,6 +1920,88 @@  xfs_iunlink_update_bucket(
 	return 0;
 }
 
+/* Set an on-disk inode's unlinked pointer and return the old value. */
+STATIC void
+xfs_iunlink_update_dinode(
+	struct xfs_trans	*tp,
+	struct xfs_buf		*ibp,
+	struct xfs_dinode	*dip,
+	struct xfs_imap		*imap,
+	xfs_ino_t		ino,
+	xfs_agino_t		new_agino)
+{
+	struct xfs_mount	*mp = tp->t_mountp;
+	int			offset;
+
+	ASSERT(new_agino == NULLAGINO ||
+	       xfs_verify_agino(mp, XFS_INO_TO_AGNO(mp, ino), new_agino));
+
+	trace_xfs_iunlink_update_dinode(mp,
+			XFS_INO_TO_AGNO(mp, ino),
+			XFS_INO_TO_AGINO(mp, ino),
+			be32_to_cpu(dip->di_next_unlinked), new_agino);
+
+	dip->di_next_unlinked = cpu_to_be32(new_agino);
+	offset = imap->im_boffset +
+			offsetof(struct xfs_dinode, di_next_unlinked);
+
+	/* need to recalc the inode CRC if appropriate */
+	xfs_dinode_calc_crc(mp, dip);
+	xfs_trans_inode_buf(tp, ibp);
+	xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1));
+	xfs_inobp_check(mp, ibp);
+}
+
+/* Set an in-core inode's unlinked pointer and return the old value. */
+STATIC int
+xfs_iunlink_update_inode(
+	struct xfs_trans	*tp,
+	struct xfs_inode	*ip,
+	xfs_agino_t		new_agino,
+	xfs_agino_t		*old_agino)
+{
+	struct xfs_mount	*mp = tp->t_mountp;
+	struct xfs_dinode	*dip;
+	struct xfs_buf		*ibp;
+	xfs_agnumber_t		agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+	xfs_agino_t		old_value;
+	int			error;
+
+	ASSERT(new_agino == NULLAGINO || xfs_verify_agino(mp, agno, new_agino));
+
+	error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp, 0, 0);
+	if (error)
+		return error;
+
+	/* Make sure the old pointer isn't garbage. */
+	old_value = be32_to_cpu(dip->di_next_unlinked);
+	if (old_value != NULLAGINO && !xfs_verify_agino(mp, agno, old_value)) {
+		error = -EFSCORRUPTED;
+		goto out;
+	}
+	*old_agino = old_value;
+
+	/*
+	 * Since we're updating a linked list, we should never find that the
+	 * current pointer is the same as the new value, unless we're
+	 * terminating the list.
+	 */
+	*old_agino = old_value;
+	if (old_value == new_agino) {
+		if (new_agino != NULLAGINO)
+			error = -EFSCORRUPTED;
+		goto out;
+	}
+
+	/* Ok, update the new pointer. */
+	xfs_iunlink_update_dinode(tp, ibp, dip, &ip->i_imap, ip->i_ino,
+			new_agino);
+	return 0;
+out:
+	xfs_trans_brelse(tp, ibp);
+	return error;
+}
+
 /*
  * This is called when the inode's link count goes to 0 or we are creating a
  * tmpfile via O_TMPFILE. In the case of a tmpfile, @ignore_linkcount will be
@@ -1937,15 +2019,12 @@  xfs_iunlink(
 {
 	struct xfs_mount	*mp = tp->t_mountp;
 	struct xfs_agi		*agi;
-	struct xfs_dinode	*dip;
 	struct xfs_buf		*agibp;
-	struct xfs_buf		*ibp;
 	struct xfs_perag	*pag;
 	xfs_agnumber_t		agno;
 	xfs_agino_t		next_agino;
 	xfs_agino_t		agino;
 	short			bucket_index;
-	int			offset;
 	int			error;
 
 	ASSERT(VFS_I(ip)->i_mode != 0);
@@ -1980,29 +2059,17 @@  xfs_iunlink(
 	}
 
 	if (next_agino != NULLAGINO) {
+		xfs_agino_t	old_agino;
+
 		/*
 		 * There is already another inode in the bucket we need
 		 * to add ourselves to.  Add us at the front of the list.
-		 * Here we put the head pointer into our next pointer,
-		 * and then we fall through to point the head at us.
 		 */
-		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
-				       0, 0);
+		error = xfs_iunlink_update_inode(tp, ip, next_agino,
+				&old_agino);
 		if (error)
 			goto out_unlock;
-
-		ASSERT(dip->di_next_unlinked == cpu_to_be32(NULLAGINO));
-		dip->di_next_unlinked = agi->agi_unlinked[bucket_index];
-		offset = ip->i_imap.im_boffset +
-			offsetof(xfs_dinode_t, di_next_unlinked);
-
-		/* need to recalc the inode CRC if appropriate */
-		xfs_dinode_calc_crc(mp, dip);
-
-		xfs_trans_inode_buf(tp, ibp);
-		xfs_trans_log_buf(tp, ibp, offset,
-				  (offset + sizeof(xfs_agino_t) - 1));
-		xfs_inobp_check(mp, ibp);
+		ASSERT(old_agino == NULLAGINO);
 	}
 
 	/* Point the head of the list to point to this inode. */
@@ -2027,9 +2094,7 @@  xfs_iunlink_remove(
 {
 	struct xfs_mount	*mp = tp->t_mountp;
 	struct xfs_agi		*agi;
-	struct xfs_dinode	*dip;
 	struct xfs_buf		*agibp;
-	struct xfs_buf		*ibp;
 	struct xfs_buf		*last_ibp;
 	struct xfs_dinode	*last_dip = NULL;
 	struct xfs_perag	*pag;
@@ -2038,8 +2103,6 @@  xfs_iunlink_remove(
 	xfs_agino_t		agino;
 	xfs_agino_t		next_agino;
 	short			bucket_index;
-	int			offset;
-	int			last_offset = 0;
 	int			error;
 
 	if (!xfs_verify_ino(mp, ip->i_ino))
@@ -2076,34 +2139,11 @@  xfs_iunlink_remove(
 		/*
 		 * We're at the head of the list.  Get the inode's on-disk
 		 * buffer to see if there is anyone after us on the list.
-		 * Only modify our next pointer if it is not already NULLAGINO.
-		 * This saves us the overhead of dealing with the buffer when
-		 * there is no need to change it.
 		 */
-		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
-				       0, 0);
-		if (error) {
-			xfs_warn(mp, "%s: xfs_imap_to_bp returned error %d.",
-				__func__, error);
+		error = xfs_iunlink_update_inode(tp, ip, NULLAGINO,
+				&next_agino);
+		if (error)
 			goto out_unlock;
-		}
-		next_agino = be32_to_cpu(dip->di_next_unlinked);
-		ASSERT(next_agino != 0);
-		if (next_agino != NULLAGINO) {
-			dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
-			offset = ip->i_imap.im_boffset +
-				offsetof(xfs_dinode_t, di_next_unlinked);
-
-			/* need to recalc the inode CRC if appropriate */
-			xfs_dinode_calc_crc(mp, dip);
-
-			xfs_trans_inode_buf(tp, ibp);
-			xfs_trans_log_buf(tp, ibp, offset,
-					  (offset + sizeof(xfs_agino_t) - 1));
-			xfs_inobp_check(mp, ibp);
-		} else {
-			xfs_trans_brelse(tp, ibp);
-		}
 
 		/* Point the head of the list to the next unlinked inode. */
 		error = xfs_iunlink_update_bucket(tp, agibp, bucket_index,
@@ -2111,13 +2151,13 @@  xfs_iunlink_remove(
 		if (error)
 			goto out_unlock;
 	} else {
+		struct xfs_imap	imap;
+
 		/*
 		 * We need to search the list for the inode being freed.
 		 */
 		last_ibp = NULL;
 		while (next_agino != agino) {
-			struct xfs_imap	imap;
-
 			if (last_ibp)
 				xfs_trans_brelse(tp, last_ibp);
 
@@ -2141,7 +2181,6 @@  xfs_iunlink_remove(
 				goto out_unlock;
 			}
 
-			last_offset = imap.im_boffset;
 			next_agino = be32_to_cpu(last_dip->di_next_unlinked);
 			if (!xfs_verify_agino(mp, agno, next_agino)) {
 				XFS_CORRUPTION_ERROR(__func__,
@@ -2156,45 +2195,14 @@  xfs_iunlink_remove(
 		 * Now last_ibp points to the buffer previous to us on the
 		 * unlinked list.  Pull us from the list.
 		 */
-		error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &dip, &ibp,
-				       0, 0);
-		if (error) {
-			xfs_warn(mp, "%s: xfs_imap_to_bp(2) returned error %d.",
-				__func__, error);
+		error = xfs_iunlink_update_inode(tp, ip, NULLAGINO,
+				&next_agino);
+		if (error)
 			goto out_unlock;
-		}
-		next_agino = be32_to_cpu(dip->di_next_unlinked);
-		ASSERT(next_agino != 0);
-		ASSERT(next_agino != agino);
-		if (next_agino != NULLAGINO) {
-			dip->di_next_unlinked = cpu_to_be32(NULLAGINO);
-			offset = ip->i_imap.im_boffset +
-				offsetof(xfs_dinode_t, di_next_unlinked);
-
-			/* need to recalc the inode CRC if appropriate */
-			xfs_dinode_calc_crc(mp, dip);
-
-			xfs_trans_inode_buf(tp, ibp);
-			xfs_trans_log_buf(tp, ibp, offset,
-					  (offset + sizeof(xfs_agino_t) - 1));
-			xfs_inobp_check(mp, ibp);
-		} else {
-			xfs_trans_brelse(tp, ibp);
-		}
-		/*
-		 * Point the previous inode on the list to the next inode.
-		 */
-		last_dip->di_next_unlinked = cpu_to_be32(next_agino);
-		ASSERT(next_agino != 0);
-		offset = last_offset + offsetof(xfs_dinode_t, di_next_unlinked);
-
-		/* need to recalc the inode CRC if appropriate */
-		xfs_dinode_calc_crc(mp, last_dip);
 
-		xfs_trans_inode_buf(tp, last_ibp);
-		xfs_trans_log_buf(tp, last_ibp, offset,
-				  (offset + sizeof(xfs_agino_t) - 1));
-		xfs_inobp_check(mp, last_ibp);
+		/* Point the previous inode on the list to the next inode. */
+		xfs_iunlink_update_dinode(tp, last_ibp, last_dip, &imap,
+				next_ino, next_agino);
 	}
 	pag->pagi_unlinked_count--;
 
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index c10478e7e49a..fbec8f0e1a9a 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3397,6 +3397,32 @@  TRACE_EVENT(xfs_iunlink_update_bucket,
 		  __entry->new_ptr)
 );
 
+TRACE_EVENT(xfs_iunlink_update_dinode,
+	TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino,
+		 xfs_agino_t old_ptr, xfs_agino_t new_ptr),
+	TP_ARGS(mp, agno, agino, old_ptr, new_ptr),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(xfs_agnumber_t, agno)
+		__field(xfs_agino_t, agino)
+		__field(xfs_agino_t, old_ptr)
+		__field(xfs_agino_t, new_ptr)
+	),
+	TP_fast_assign(
+		__entry->dev = mp->m_super->s_dev;
+		__entry->agno = agno;
+		__entry->agino = agino;
+		__entry->old_ptr = old_ptr;
+		__entry->new_ptr = new_ptr;
+	),
+	TP_printk("dev %d:%d agno %u agino 0x%x old 0x%x new 0x%x",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->agno,
+		  __entry->agino,
+		  __entry->old_ptr,
+		  __entry->new_ptr)
+);
+
 #endif /* _TRACE_XFS_H */
 
 #undef TRACE_INCLUDE_PATH