xfs: flush eof/cowblocks if we can't reserve quota for file blocks
authorDarrick J. Wong <djwong@kernel.org>
Sat, 23 Jan 2021 00:48:37 +0000 (16:48 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 3 Feb 2021 17:18:49 +0000 (09:18 -0800)
If a fs modification (data write, reflink, xattr set, fallocate, etc.)
is unable to reserve enough quota to handle the modification, try
clearing whatever space the filesystem might have been hanging onto in
the hopes of speeding up the filesystem.  The flushing behavior will
become particularly important when we add deferred inode inactivation
because that will increase the amount of space that isn't actively tied
to user data.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Brian Foster <bfoster@redhat.com>
fs/xfs/xfs_reflink.c
fs/xfs/xfs_trans.c

index 086866f6e71fb19e86a33d8992b6e7b83e308dc1..725c7d8e44381bd1f5efa33e695b1169b0e6c02d 100644 (file)
@@ -1092,6 +1092,11 @@ xfs_reflink_remap_extent(
         * count.  This is suboptimal, but the VFS flushed the dest range
         * before we started.  That should have removed all the delalloc
         * reservations, but we code defensively.
+        *
+        * xfs_trans_alloc_inode above already tried to grab an even larger
+        * quota reservation, and kicked off a blockgc scan if it couldn't.
+        * If we can't get a potentially smaller quota reservation now, we're
+        * done.
         */
        if (!quota_reserved && !smap_real && dmap_written) {
                error = xfs_trans_reserve_quota_nblks(tp, ip,
index 29dca1bc4c1a93f290b21ff973b7319e44c4285b..4071bbed2d48cd6c7ad1daab5e24cba417ef0fdd 100644 (file)
@@ -23,6 +23,7 @@
 #include "xfs_inode.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_icache.h"
 
 kmem_zone_t    *xfs_trans_zone;
 
@@ -1046,8 +1047,10 @@ xfs_trans_alloc_inode(
 {
        struct xfs_trans        *tp;
        struct xfs_mount        *mp = ip->i_mount;
+       bool                    retried = false;
        int                     error;
 
+retry:
        error = xfs_trans_alloc(mp, resv, dblocks,
                        rblocks / mp->m_sb.sb_rextsize,
                        force ? XFS_TRANS_RESERVE : 0, &tp);
@@ -1065,6 +1068,13 @@ xfs_trans_alloc_inode(
        }
 
        error = xfs_trans_reserve_quota_nblks(tp, ip, dblocks, rblocks, force);
+       if ((error == -EDQUOT || error == -ENOSPC) && !retried) {
+               xfs_trans_cancel(tp);
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               xfs_blockgc_free_quota(ip, 0);
+               retried = true;
+               goto retry;
+       }
        if (error)
                goto out_cancel;