diff mbox series

xfs: Allow scrub to detect inodes with non-maximal sized extents

Message ID 20210223082629.16719-1-chandanrlinux@gmail.com (mailing list archive)
State New
Headers show
Series xfs: Allow scrub to detect inodes with non-maximal sized extents | expand

Commit Message

Chandan Babu R Feb. 23, 2021, 8:26 a.m. UTC
This commit now makes it possible for scrub to check if an inode's extents are
maximally sized i.e. it checks if an inode's extent is contiguous (in terms of
both file offset and disk offset) with neighbouring extents and the total
length of both the extents is less than the maximum allowed extent
length (i.e. MAXEXTLEN).

Signed-off-by: Chandan Babu R <chandanrlinux@gmail.com>
---
 fs/xfs/scrub/inode.c   | 11 +++++++----
 fs/xfs/xfs_bmap_util.c | 36 +++++++++++++++++++++++++++++++-----
 fs/xfs/xfs_bmap_util.h |  5 +++--
 fs/xfs/xfs_qm.c        |  2 +-
 4 files changed, 42 insertions(+), 12 deletions(-)

Comments

Darrick J. Wong Feb. 23, 2021, 5:01 p.m. UTC | #1
On Tue, Feb 23, 2021 at 01:56:29PM +0530, Chandan Babu R wrote:
> This commit now makes it possible for scrub to check if an inode's extents are
> maximally sized i.e. it checks if an inode's extent is contiguous (in terms of
> both file offset and disk offset) with neighbouring extents and the total
> length of both the extents is less than the maximum allowed extent
> length (i.e. MAXEXTLEN).
> 
> Signed-off-by: Chandan Babu R <chandanrlinux@gmail.com>
> ---
>  fs/xfs/scrub/inode.c   | 11 +++++++----
>  fs/xfs/xfs_bmap_util.c | 36 +++++++++++++++++++++++++++++++-----
>  fs/xfs/xfs_bmap_util.h |  5 +++--
>  fs/xfs/xfs_qm.c        |  2 +-
>  4 files changed, 42 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index faf65eb5bd31..33b530785e36 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -478,23 +478,26 @@ xchk_inode_xref_bmap(
>  	xfs_filblks_t		count;
>  	xfs_filblks_t		acount;
>  	int			error;
> +	bool			maximal_sized_exts;
>  
>  	if (xchk_skip_xref(sc->sm))
>  		return;
>  
>  	/* Walk all the extents to check nextents/naextents/nblocks. */
> +	maximal_sized_exts = true;

Shouldn't this be set by xfs_bmap_count_blocks()?  It's purely an out
parameter.

>  	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
> -			&nextents, &count);
> +			&nextents, &count, &maximal_sized_exts);
>  	if (!xchk_should_check_xref(sc, &error, NULL))
>  		return;
> -	if (nextents < be32_to_cpu(dip->di_nextents))
> +	if (nextents < be32_to_cpu(dip->di_nextents) || !maximal_sized_exts)
>  		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  
> +	maximal_sized_exts = true;
>  	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
> -			&nextents, &acount);
> +			&nextents, &acount, &maximal_sized_exts);
>  	if (!xchk_should_check_xref(sc, &error, NULL))
>  		return;
> -	if (nextents != be16_to_cpu(dip->di_anextents))
> +	if (nextents != be16_to_cpu(dip->di_anextents) || !maximal_sized_exts)
>  		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  
>  	/* Check nblocks against the inode. */
> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index e7d68318e6a5..eca39677a46f 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -176,6 +176,25 @@ xfs_bmap_rtalloc(
>   * Extent tree block counting routines.
>   */
>  
> +STATIC bool
> +is_maximal_extent(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*icur,
> +	struct xfs_bmbt_irec	*got)
> +{
> +	struct xfs_bmbt_irec	left;
> +
> +	if (xfs_iext_peek_prev_extent(ifp, icur, &left) &&
> +		!isnullstartblock(left.br_startblock) &&
> +		left.br_startoff + left.br_blockcount == got->br_startoff &&
> +		left.br_startblock + left.br_blockcount == got->br_startblock &&
> +		left.br_state == got->br_state &&
> +		left.br_blockcount + got->br_blockcount <= MAXEXTLEN)
> +		return false;

Please don't align the if test code with the statement body.

> +	else

No need for the else here.

> +		return true;
> +}
> +
>  /*
>   * Count leaf blocks given a range of extent records.  Delayed allocation
>   * extents are not counted towards the totals.
> @@ -183,7 +202,8 @@ xfs_bmap_rtalloc(
>  xfs_extnum_t
>  xfs_bmap_count_leaves(
>  	struct xfs_ifork	*ifp,
> -	xfs_filblks_t		*count)
> +	xfs_filblks_t		*count,
> +	bool			*maximal_sized_exts)
>  {
>  	struct xfs_iext_cursor	icur;
>  	struct xfs_bmbt_irec	got;
> @@ -191,6 +211,10 @@ xfs_bmap_count_leaves(
>  
>  	for_each_xfs_iext(ifp, &icur, &got) {
>  		if (!isnullstartblock(got.br_startblock)) {
> +			if (maximal_sized_exts)
> +				*maximal_sized_exts =
> +					is_maximal_extent(ifp, &icur, &got);

Shouldn't this be *maximal_sized_exts &= is_maximal_extent(); ?

Otherwise this only tells us if the last extent in the fork is maximal.

I also wonder if it's really worth the extra complexity to make the
boolean an optional argument, since there are callers that pass in a
count pointer that's a dummy value.

--D

> +
>  			*count += got.br_blockcount;
>  			numrecs++;
>  		}
> @@ -209,7 +233,8 @@ xfs_bmap_count_blocks(
>  	struct xfs_inode	*ip,
>  	int			whichfork,
>  	xfs_extnum_t		*nextents,
> -	xfs_filblks_t		*count)
> +	xfs_filblks_t		*count,
> +	bool			*maximal_sized_exts)
>  {
>  	struct xfs_mount	*mp = ip->i_mount;
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
> @@ -246,7 +271,8 @@ xfs_bmap_count_blocks(
>  
>  		/* fall through */
>  	case XFS_DINODE_FMT_EXTENTS:
> -		*nextents = xfs_bmap_count_leaves(ifp, count);
> +		*nextents = xfs_bmap_count_leaves(ifp, count,
> +				maximal_sized_exts);
>  		break;
>  	}
>  
> @@ -1442,14 +1468,14 @@ xfs_swap_extent_forks(
>  	if (XFS_IFORK_Q(ip) && ip->i_afp->if_nextents > 0 &&
>  	    ip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
>  		error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &junk,
> -				&aforkblks);
> +				&aforkblks, NULL);
>  		if (error)
>  			return error;
>  	}
>  	if (XFS_IFORK_Q(tip) && tip->i_afp->if_nextents > 0 &&
>  	    tip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
>  		error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, &junk,
> -				&taforkblks);
> +				&taforkblks, NULL);
>  		if (error)
>  			return error;
>  	}
> diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
> index 9f993168b55b..7b0ce227c7bd 100644
> --- a/fs/xfs/xfs_bmap_util.h
> +++ b/fs/xfs/xfs_bmap_util.h
> @@ -71,10 +71,11 @@ int	xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
>  
>  xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);
>  
> -xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count);
> +xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count,
> +		bool *maximal_sized_exts);
>  int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
>  			  int whichfork, xfs_extnum_t *nextents,
> -			  xfs_filblks_t *count);
> +			  xfs_filblks_t *count, bool *maximal_sized_exts);
>  
>  int	xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
>  			      xfs_off_t len);
> diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
> index 742d1413e2d0..04fcca5583b3 100644
> --- a/fs/xfs/xfs_qm.c
> +++ b/fs/xfs/xfs_qm.c
> @@ -1170,7 +1170,7 @@ xfs_qm_dqusage_adjust(
>  				goto error0;
>  		}
>  
> -		xfs_bmap_count_leaves(ifp, &rtblks);
> +		xfs_bmap_count_leaves(ifp, &rtblks, NULL);
>  	}
>  
>  	nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks;
> -- 
> 2.29.2
>
Dave Chinner Feb. 23, 2021, 8:30 p.m. UTC | #2
On Tue, Feb 23, 2021 at 01:56:29PM +0530, Chandan Babu R wrote:
> This commit now makes it possible for scrub to check if an inode's extents are
> maximally sized i.e. it checks if an inode's extent is contiguous (in terms of
> both file offset and disk offset) with neighbouring extents and the total
> length of both the extents is less than the maximum allowed extent
> length (i.e. MAXEXTLEN).

It took me a while to understand that what this is actually doing
(had to read the code because I couldn't work out what this meant).
Essentially, it is determining if two extents that are physically
and logically adjacent were not merged together into a single extent
when the combined size of the two extents would fit into a single
extent record.

I'm not sure this is an issue - it most definitely isn't corruption
as nothing will have any problems looking up either extent, nor
modifying or removing either extent. It's not ideal, but it isn't
corruption.

I can see how it would come about, too, because extent removal
doesn't merge remaining partial extents.

That is, create a long written extent in a file, then use fallocate
to allocate an adjacent extent that puts the two extents over
MAXEXTLEN. Now we have two phsyically and logically adjacent extents
that only differ by state. Now do a single write that converts the
entire unwritten extent to written so no merging occurs during the
state conversion.  Now punch out the far end of the second extent.

This ends up in xfs_bmap_del_extent_real(), which simply removes the
end of the second extent. It does not look up the previous extent
and attempt to merge the remaining part of the second extent into
the previous adjacent extent.

Hence, at this point, we have two logically and physically adjacent
extents whose combined length is less than MAXLEN. This patch will
now signal that as corruption, which is wrong.

> Signed-off-by: Chandan Babu R <chandanrlinux@gmail.com>
> ---
>  fs/xfs/scrub/inode.c   | 11 +++++++----
>  fs/xfs/xfs_bmap_util.c | 36 +++++++++++++++++++++++++++++++-----
>  fs/xfs/xfs_bmap_util.h |  5 +++--
>  fs/xfs/xfs_qm.c        |  2 +-
>  4 files changed, 42 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
> index faf65eb5bd31..33b530785e36 100644
> --- a/fs/xfs/scrub/inode.c
> +++ b/fs/xfs/scrub/inode.c
> @@ -478,23 +478,26 @@ xchk_inode_xref_bmap(
>  	xfs_filblks_t		count;
>  	xfs_filblks_t		acount;
>  	int			error;
> +	bool			maximal_sized_exts;
>  
>  	if (xchk_skip_xref(sc->sm))
>  		return;
>  
>  	/* Walk all the extents to check nextents/naextents/nblocks. */
> +	maximal_sized_exts = true;
>  	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
> -			&nextents, &count);
> +			&nextents, &count, &maximal_sized_exts);
>  	if (!xchk_should_check_xref(sc, &error, NULL))
>  		return;
> -	if (nextents < be32_to_cpu(dip->di_nextents))
> +	if (nextents < be32_to_cpu(dip->di_nextents) || !maximal_sized_exts)
>  		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  
> +	maximal_sized_exts = true;
>  	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
> -			&nextents, &acount);
> +			&nextents, &acount, &maximal_sized_exts);
>  	if (!xchk_should_check_xref(sc, &error, NULL))
>  		return;
> -	if (nextents != be16_to_cpu(dip->di_anextents))
> +	if (nextents != be16_to_cpu(dip->di_anextents) || !maximal_sized_exts)
>  		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
>  
>  	/* Check nblocks against the inode. */
> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index e7d68318e6a5..eca39677a46f 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -176,6 +176,25 @@ xfs_bmap_rtalloc(
>   * Extent tree block counting routines.
>   */
>  
> +STATIC bool
> +is_maximal_extent(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*icur,
> +	struct xfs_bmbt_irec	*got)
> +{
> +	struct xfs_bmbt_irec	left;
> +
> +	if (xfs_iext_peek_prev_extent(ifp, icur, &left) &&
> +		!isnullstartblock(left.br_startblock) &&
> +		left.br_startoff + left.br_blockcount == got->br_startoff &&
> +		left.br_startblock + left.br_blockcount == got->br_startblock &&
> +		left.br_state == got->br_state &&
> +		left.br_blockcount + got->br_blockcount <= MAXEXTLEN)
> +		return false;
> +	else
> +		return true;
> +}
> +
>  /*
>   * Count leaf blocks given a range of extent records.  Delayed allocation
>   * extents are not counted towards the totals.
> @@ -183,7 +202,8 @@ xfs_bmap_rtalloc(
>  xfs_extnum_t
>  xfs_bmap_count_leaves(
>  	struct xfs_ifork	*ifp,
> -	xfs_filblks_t		*count)
> +	xfs_filblks_t		*count,
> +	bool			*maximal_sized_exts)
>  {
>  	struct xfs_iext_cursor	icur;
>  	struct xfs_bmbt_irec	got;
> @@ -191,6 +211,10 @@ xfs_bmap_count_leaves(
>  
>  	for_each_xfs_iext(ifp, &icur, &got) {
>  		if (!isnullstartblock(got.br_startblock)) {
> +			if (maximal_sized_exts)
> +				*maximal_sized_exts =
> +					is_maximal_extent(ifp, &icur, &got);

This looks broken, anyway. Even if you find a "bad" extent, the
state will get overwritten by the next good extent and hence never
get reported.

Cheers,

Dave.
Chandan Babu R Feb. 24, 2021, 3:09 a.m. UTC | #3
On 24 Feb 2021 at 02:00, Dave Chinner wrote:
> On Tue, Feb 23, 2021 at 01:56:29PM +0530, Chandan Babu R wrote:
>> This commit now makes it possible for scrub to check if an inode's extents are
>> maximally sized i.e. it checks if an inode's extent is contiguous (in terms of
>> both file offset and disk offset) with neighbouring extents and the total
>> length of both the extents is less than the maximum allowed extent
>> length (i.e. MAXEXTLEN).
>
> It took me a while to understand that what this is actually doing
> (had to read the code because I couldn't work out what this meant).
> Essentially, it is determining if two extents that are physically
> and logically adjacent were not merged together into a single extent
> when the combined size of the two extents would fit into a single
> extent record.
>
> I'm not sure this is an issue - it most definitely isn't corruption
> as nothing will have any problems looking up either extent, nor
> modifying or removing either extent. It's not ideal, but it isn't
> corruption.
>
> I can see how it would come about, too, because extent removal
> doesn't merge remaining partial extents.
>
> That is, create a long written extent in a file, then use fallocate
> to allocate an adjacent extent that puts the two extents over
> MAXEXTLEN. Now we have two phsyically and logically adjacent extents
> that only differ by state. Now do a single write that converts the
> entire unwritten extent to written so no merging occurs during the
> state conversion.  Now punch out the far end of the second extent.
>
> This ends up in xfs_bmap_del_extent_real(), which simply removes the
> end of the second extent. It does not look up the previous extent
> and attempt to merge the remaining part of the second extent into
> the previous adjacent extent.
>
> Hence, at this point, we have two logically and physically adjacent
> extents whose combined length is less than MAXLEN. This patch will
> now signal that as corruption, which is wrong.

Ah ok. Thanks for explaining the scenario which could lead to a false positive
report because of the code changes in this patch.

--
chandan
diff mbox series

Patch

diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index faf65eb5bd31..33b530785e36 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -478,23 +478,26 @@  xchk_inode_xref_bmap(
 	xfs_filblks_t		count;
 	xfs_filblks_t		acount;
 	int			error;
+	bool			maximal_sized_exts;
 
 	if (xchk_skip_xref(sc->sm))
 		return;
 
 	/* Walk all the extents to check nextents/naextents/nblocks. */
+	maximal_sized_exts = true;
 	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
-			&nextents, &count);
+			&nextents, &count, &maximal_sized_exts);
 	if (!xchk_should_check_xref(sc, &error, NULL))
 		return;
-	if (nextents < be32_to_cpu(dip->di_nextents))
+	if (nextents < be32_to_cpu(dip->di_nextents) || !maximal_sized_exts)
 		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
 
+	maximal_sized_exts = true;
 	error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
-			&nextents, &acount);
+			&nextents, &acount, &maximal_sized_exts);
 	if (!xchk_should_check_xref(sc, &error, NULL))
 		return;
-	if (nextents != be16_to_cpu(dip->di_anextents))
+	if (nextents != be16_to_cpu(dip->di_anextents) || !maximal_sized_exts)
 		xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);
 
 	/* Check nblocks against the inode. */
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index e7d68318e6a5..eca39677a46f 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -176,6 +176,25 @@  xfs_bmap_rtalloc(
  * Extent tree block counting routines.
  */
 
+STATIC bool
+is_maximal_extent(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*icur,
+	struct xfs_bmbt_irec	*got)
+{
+	struct xfs_bmbt_irec	left;
+
+	if (xfs_iext_peek_prev_extent(ifp, icur, &left) &&
+		!isnullstartblock(left.br_startblock) &&
+		left.br_startoff + left.br_blockcount == got->br_startoff &&
+		left.br_startblock + left.br_blockcount == got->br_startblock &&
+		left.br_state == got->br_state &&
+		left.br_blockcount + got->br_blockcount <= MAXEXTLEN)
+		return false;
+	else
+		return true;
+}
+
 /*
  * Count leaf blocks given a range of extent records.  Delayed allocation
  * extents are not counted towards the totals.
@@ -183,7 +202,8 @@  xfs_bmap_rtalloc(
 xfs_extnum_t
 xfs_bmap_count_leaves(
 	struct xfs_ifork	*ifp,
-	xfs_filblks_t		*count)
+	xfs_filblks_t		*count,
+	bool			*maximal_sized_exts)
 {
 	struct xfs_iext_cursor	icur;
 	struct xfs_bmbt_irec	got;
@@ -191,6 +211,10 @@  xfs_bmap_count_leaves(
 
 	for_each_xfs_iext(ifp, &icur, &got) {
 		if (!isnullstartblock(got.br_startblock)) {
+			if (maximal_sized_exts)
+				*maximal_sized_exts =
+					is_maximal_extent(ifp, &icur, &got);
+
 			*count += got.br_blockcount;
 			numrecs++;
 		}
@@ -209,7 +233,8 @@  xfs_bmap_count_blocks(
 	struct xfs_inode	*ip,
 	int			whichfork,
 	xfs_extnum_t		*nextents,
-	xfs_filblks_t		*count)
+	xfs_filblks_t		*count,
+	bool			*maximal_sized_exts)
 {
 	struct xfs_mount	*mp = ip->i_mount;
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
@@ -246,7 +271,8 @@  xfs_bmap_count_blocks(
 
 		/* fall through */
 	case XFS_DINODE_FMT_EXTENTS:
-		*nextents = xfs_bmap_count_leaves(ifp, count);
+		*nextents = xfs_bmap_count_leaves(ifp, count,
+				maximal_sized_exts);
 		break;
 	}
 
@@ -1442,14 +1468,14 @@  xfs_swap_extent_forks(
 	if (XFS_IFORK_Q(ip) && ip->i_afp->if_nextents > 0 &&
 	    ip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
 		error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &junk,
-				&aforkblks);
+				&aforkblks, NULL);
 		if (error)
 			return error;
 	}
 	if (XFS_IFORK_Q(tip) && tip->i_afp->if_nextents > 0 &&
 	    tip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
 		error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, &junk,
-				&taforkblks);
+				&taforkblks, NULL);
 		if (error)
 			return error;
 	}
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index 9f993168b55b..7b0ce227c7bd 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -71,10 +71,11 @@  int	xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
 
 xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);
 
-xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count);
+xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count,
+		bool *maximal_sized_exts);
 int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
 			  int whichfork, xfs_extnum_t *nextents,
-			  xfs_filblks_t *count);
+			  xfs_filblks_t *count, bool *maximal_sized_exts);
 
 int	xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
 			      xfs_off_t len);
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 742d1413e2d0..04fcca5583b3 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1170,7 +1170,7 @@  xfs_qm_dqusage_adjust(
 				goto error0;
 		}
 
-		xfs_bmap_count_leaves(ifp, &rtblks);
+		xfs_bmap_count_leaves(ifp, &rtblks, NULL);
 	}
 
 	nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks;