* [PATCH 01/11] xfs: trigger all block gc scans when low on quota space
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
@ 2021-01-28 6:02 ` Darrick J. Wong
2021-01-28 6:02 ` [PATCH 02/11] xfs: don't stall cowblocks scan if we can't take locks Darrick J. Wong
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:02 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, Brian Foster, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
The functions to run an eof/cowblocks scan to try to reduce quota usage
are kind of a mess -- the logic repeatedly initializes an eofb structure
and there are logic bugs in the code that result in the cowblocks scan
never actually happening.
Replace all three functions with a single function that fills out an
eofb and runs both eof and cowblocks scans.
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_file.c | 15 ++++++---------
fs/xfs/xfs_icache.c | 46 ++++++++++++++++------------------------------
fs/xfs/xfs_icache.h | 4 ++--
3 files changed, 24 insertions(+), 41 deletions(-)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 5d4a66c72c78..69879237533b 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -713,7 +713,7 @@ xfs_file_buffered_write(
struct inode *inode = mapping->host;
struct xfs_inode *ip = XFS_I(inode);
ssize_t ret;
- int enospc = 0;
+ bool cleared_space = false;
int iolock;
if (iocb->ki_flags & IOCB_NOWAIT)
@@ -745,19 +745,16 @@ xfs_file_buffered_write(
* also behaves as a filter to prevent too many eofblocks scans from
* running at the same time.
*/
- if (ret == -EDQUOT && !enospc) {
+ if (ret == -EDQUOT && !cleared_space) {
xfs_iunlock(ip, iolock);
- enospc = xfs_inode_free_quota_eofblocks(ip);
- if (enospc)
- goto write_retry;
- enospc = xfs_inode_free_quota_cowblocks(ip);
- if (enospc)
+ cleared_space = xfs_inode_free_quota_blocks(ip);
+ if (cleared_space)
goto write_retry;
iolock = 0;
- } else if (ret == -ENOSPC && !enospc) {
+ } else if (ret == -ENOSPC && !cleared_space) {
struct xfs_eofblocks eofb = {0};
- enospc = 1;
+ cleared_space = true;
xfs_flush_inodes(ip->i_mount);
xfs_iunlock(ip, iolock);
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index deb99300d171..c71eb15e3835 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1397,33 +1397,31 @@ xfs_icache_free_eofblocks(
}
/*
- * Run eofblocks scans on the quotas applicable to the inode. For inodes with
- * multiple quotas, we don't know exactly which quota caused an allocation
+ * Run cow/eofblocks scans on the quotas applicable to the inode. For inodes
+ * with multiple quotas, we don't know exactly which quota caused an allocation
* failure. We make a best effort by including each quota under low free space
* conditions (less than 1% free space) in the scan.
*/
-static int
-__xfs_inode_free_quota_eofblocks(
- struct xfs_inode *ip,
- int (*execute)(struct xfs_mount *mp,
- struct xfs_eofblocks *eofb))
+bool
+xfs_inode_free_quota_blocks(
+ struct xfs_inode *ip)
{
- int scan = 0;
- struct xfs_eofblocks eofb = {0};
- struct xfs_dquot *dq;
+ struct xfs_eofblocks eofb = {0};
+ struct xfs_dquot *dq;
+ bool do_work = false;
/*
* Run a sync scan to increase effectiveness and use the union filter to
* cover all applicable quotas in a single scan.
*/
- eofb.eof_flags = XFS_EOF_FLAGS_UNION|XFS_EOF_FLAGS_SYNC;
+ eofb.eof_flags = XFS_EOF_FLAGS_UNION | XFS_EOF_FLAGS_SYNC;
if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
dq = xfs_inode_dquot(ip, XFS_DQTYPE_USER);
if (dq && xfs_dquot_lowsp(dq)) {
eofb.eof_uid = VFS_I(ip)->i_uid;
eofb.eof_flags |= XFS_EOF_FLAGS_UID;
- scan = 1;
+ do_work = true;
}
}
@@ -1432,21 +1430,16 @@ __xfs_inode_free_quota_eofblocks(
if (dq && xfs_dquot_lowsp(dq)) {
eofb.eof_gid = VFS_I(ip)->i_gid;
eofb.eof_flags |= XFS_EOF_FLAGS_GID;
- scan = 1;
+ do_work = true;
}
}
- if (scan)
- execute(ip->i_mount, &eofb);
+ if (!do_work)
+ return false;
- return scan;
-}
-
-int
-xfs_inode_free_quota_eofblocks(
- struct xfs_inode *ip)
-{
- return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_eofblocks);
+ xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+ xfs_icache_free_cowblocks(ip->i_mount, &eofb);
+ return true;
}
static inline unsigned long
@@ -1646,13 +1639,6 @@ xfs_icache_free_cowblocks(
XFS_ICI_COWBLOCKS_TAG);
}
-int
-xfs_inode_free_quota_cowblocks(
- struct xfs_inode *ip)
-{
- return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_cowblocks);
-}
-
void
xfs_inode_set_cowblocks_tag(
xfs_inode_t *ip)
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index 3a4c8b382cd0..3f7ddbca8638 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -54,17 +54,17 @@ long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan);
void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
+bool xfs_inode_free_quota_blocks(struct xfs_inode *ip);
+
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
int xfs_icache_free_eofblocks(struct xfs_mount *, struct xfs_eofblocks *);
-int xfs_inode_free_quota_eofblocks(struct xfs_inode *ip);
void xfs_eofblocks_worker(struct work_struct *);
void xfs_queue_eofblocks(struct xfs_mount *);
void xfs_inode_set_cowblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_cowblocks_tag(struct xfs_inode *ip);
int xfs_icache_free_cowblocks(struct xfs_mount *, struct xfs_eofblocks *);
-int xfs_inode_free_quota_cowblocks(struct xfs_inode *ip);
void xfs_cowblocks_worker(struct work_struct *);
void xfs_queue_cowblocks(struct xfs_mount *);
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 02/11] xfs: don't stall cowblocks scan if we can't take locks
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
2021-01-28 6:02 ` [PATCH 01/11] xfs: trigger all block gc scans when low on quota space Darrick J. Wong
@ 2021-01-28 6:02 ` Darrick J. Wong
2021-01-28 6:02 ` [PATCH 03/11] xfs: xfs_inode_free_quota_blocks should scan project quota Darrick J. Wong
` (8 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:02 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
Don't stall the cowblocks scan on a locked inode if we possibly can.
We'd much rather the background scanner keep moving.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/xfs_icache.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index c71eb15e3835..89f9e692fde7 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1605,17 +1605,31 @@ xfs_inode_free_cowblocks(
void *args)
{
struct xfs_eofblocks *eofb = args;
+ bool wait;
int ret = 0;
+ wait = eofb && (eofb->eof_flags & XFS_EOF_FLAGS_SYNC);
+
if (!xfs_prep_free_cowblocks(ip))
return 0;
if (!xfs_inode_matches_eofb(ip, eofb))
return 0;
- /* Free the CoW blocks */
- xfs_ilock(ip, XFS_IOLOCK_EXCL);
- xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
+ /*
+ * If the caller is waiting, return -EAGAIN to keep the background
+ * scanner moving and revisit the inode in a subsequent pass.
+ */
+ if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) {
+ if (wait)
+ return -EAGAIN;
+ return 0;
+ }
+ if (!xfs_ilock_nowait(ip, XFS_MMAPLOCK_EXCL)) {
+ if (wait)
+ ret = -EAGAIN;
+ goto out_iolock;
+ }
/*
* Check again, nobody else should be able to dirty blocks or change
@@ -1625,6 +1639,7 @@ xfs_inode_free_cowblocks(
ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF, false);
xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
+out_iolock:
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return ret;
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 03/11] xfs: xfs_inode_free_quota_blocks should scan project quota
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
2021-01-28 6:02 ` [PATCH 01/11] xfs: trigger all block gc scans when low on quota space Darrick J. Wong
2021-01-28 6:02 ` [PATCH 02/11] xfs: don't stall cowblocks scan if we can't take locks Darrick J. Wong
@ 2021-01-28 6:02 ` Darrick J. Wong
2021-01-28 6:02 ` [PATCH 04/11] xfs: move and rename xfs_inode_free_quota_blocks to avoid conflicts Darrick J. Wong
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:02 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, Brian Foster, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
Buffered writers who have run out of quota reservation call
xfs_inode_free_quota_blocks to try to free any space reservations that
might reduce the quota usage. Unfortunately, the buffered write path
treats "out of project quota" the same as "out of overall space" so this
function has never supported scanning for space that might ease an "out
of project quota" condition.
We're about to start using this function for cases where we actually
/can/ tell if we're out of project quota, so add in this functionality.
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_icache.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 89f9e692fde7..10c1a0dee17d 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1434,6 +1434,15 @@ xfs_inode_free_quota_blocks(
}
}
+ if (XFS_IS_PQUOTA_ENFORCED(ip->i_mount)) {
+ dq = xfs_inode_dquot(ip, XFS_DQTYPE_PROJ);
+ if (dq && xfs_dquot_lowsp(dq)) {
+ eofb.eof_prid = ip->i_d.di_projid;
+ eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
+ do_work = true;
+ }
+ }
+
if (!do_work)
return false;
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 04/11] xfs: move and rename xfs_inode_free_quota_blocks to avoid conflicts
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (2 preceding siblings ...)
2021-01-28 6:02 ` [PATCH 03/11] xfs: xfs_inode_free_quota_blocks should scan project quota Darrick J. Wong
@ 2021-01-28 6:02 ` Darrick J. Wong
2021-01-28 6:02 ` [PATCH 05/11] xfs: pass flags and return gc errors from xfs_blockgc_free_quota Darrick J. Wong
` (6 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:02 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, Brian Foster, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
Move this function further down in the file so that later cleanups won't
have to declare static functions. Change the name because we're about
to rework all the code that performs garbage collection of speculatively
allocated file blocks. No functional changes.
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_file.c | 2 -
fs/xfs/xfs_icache.c | 110 ++++++++++++++++++++++++++-------------------------
fs/xfs/xfs_icache.h | 2 -
3 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 69879237533b..d69e5abcc1b4 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -747,7 +747,7 @@ xfs_file_buffered_write(
*/
if (ret == -EDQUOT && !cleared_space) {
xfs_iunlock(ip, iolock);
- cleared_space = xfs_inode_free_quota_blocks(ip);
+ cleared_space = xfs_blockgc_free_quota(ip);
if (cleared_space)
goto write_retry;
iolock = 0;
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 10c1a0dee17d..aba901d5637b 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1396,61 +1396,6 @@ xfs_icache_free_eofblocks(
XFS_ICI_EOFBLOCKS_TAG);
}
-/*
- * Run cow/eofblocks scans on the quotas applicable to the inode. For inodes
- * with multiple quotas, we don't know exactly which quota caused an allocation
- * failure. We make a best effort by including each quota under low free space
- * conditions (less than 1% free space) in the scan.
- */
-bool
-xfs_inode_free_quota_blocks(
- struct xfs_inode *ip)
-{
- struct xfs_eofblocks eofb = {0};
- struct xfs_dquot *dq;
- bool do_work = false;
-
- /*
- * Run a sync scan to increase effectiveness and use the union filter to
- * cover all applicable quotas in a single scan.
- */
- eofb.eof_flags = XFS_EOF_FLAGS_UNION | XFS_EOF_FLAGS_SYNC;
-
- if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
- dq = xfs_inode_dquot(ip, XFS_DQTYPE_USER);
- if (dq && xfs_dquot_lowsp(dq)) {
- eofb.eof_uid = VFS_I(ip)->i_uid;
- eofb.eof_flags |= XFS_EOF_FLAGS_UID;
- do_work = true;
- }
- }
-
- if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) {
- dq = xfs_inode_dquot(ip, XFS_DQTYPE_GROUP);
- if (dq && xfs_dquot_lowsp(dq)) {
- eofb.eof_gid = VFS_I(ip)->i_gid;
- eofb.eof_flags |= XFS_EOF_FLAGS_GID;
- do_work = true;
- }
- }
-
- if (XFS_IS_PQUOTA_ENFORCED(ip->i_mount)) {
- dq = xfs_inode_dquot(ip, XFS_DQTYPE_PROJ);
- if (dq && xfs_dquot_lowsp(dq)) {
- eofb.eof_prid = ip->i_d.di_projid;
- eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
- do_work = true;
- }
- }
-
- if (!do_work)
- return false;
-
- xfs_icache_free_eofblocks(ip->i_mount, &eofb);
- xfs_icache_free_cowblocks(ip->i_mount, &eofb);
- return true;
-}
-
static inline unsigned long
xfs_iflag_for_tag(
int tag)
@@ -1699,3 +1644,58 @@ xfs_start_block_reaping(
xfs_queue_eofblocks(mp);
xfs_queue_cowblocks(mp);
}
+
+/*
+ * Run cow/eofblocks scans on the quotas applicable to the inode. For inodes
+ * with multiple quotas, we don't know exactly which quota caused an allocation
+ * failure. We make a best effort by including each quota under low free space
+ * conditions (less than 1% free space) in the scan.
+ */
+bool
+xfs_blockgc_free_quota(
+ struct xfs_inode *ip)
+{
+ struct xfs_eofblocks eofb = {0};
+ struct xfs_dquot *dq;
+ bool do_work = false;
+
+ /*
+ * Run a sync scan to increase effectiveness and use the union filter to
+ * cover all applicable quotas in a single scan.
+ */
+ eofb.eof_flags = XFS_EOF_FLAGS_UNION | XFS_EOF_FLAGS_SYNC;
+
+ if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
+ dq = xfs_inode_dquot(ip, XFS_DQTYPE_USER);
+ if (dq && xfs_dquot_lowsp(dq)) {
+ eofb.eof_uid = VFS_I(ip)->i_uid;
+ eofb.eof_flags |= XFS_EOF_FLAGS_UID;
+ do_work = true;
+ }
+ }
+
+ if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) {
+ dq = xfs_inode_dquot(ip, XFS_DQTYPE_GROUP);
+ if (dq && xfs_dquot_lowsp(dq)) {
+ eofb.eof_gid = VFS_I(ip)->i_gid;
+ eofb.eof_flags |= XFS_EOF_FLAGS_GID;
+ do_work = true;
+ }
+ }
+
+ if (XFS_IS_PQUOTA_ENFORCED(ip->i_mount)) {
+ dq = xfs_inode_dquot(ip, XFS_DQTYPE_PROJ);
+ if (dq && xfs_dquot_lowsp(dq)) {
+ eofb.eof_prid = ip->i_d.di_projid;
+ eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
+ do_work = true;
+ }
+ }
+
+ if (!do_work)
+ return false;
+
+ xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+ xfs_icache_free_cowblocks(ip->i_mount, &eofb);
+ return true;
+}
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index 3f7ddbca8638..21b726a05b0d 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -54,7 +54,7 @@ long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan);
void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
-bool xfs_inode_free_quota_blocks(struct xfs_inode *ip);
+bool xfs_blockgc_free_quota(struct xfs_inode *ip);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 05/11] xfs: pass flags and return gc errors from xfs_blockgc_free_quota
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (3 preceding siblings ...)
2021-01-28 6:02 ` [PATCH 04/11] xfs: move and rename xfs_inode_free_quota_blocks to avoid conflicts Darrick J. Wong
@ 2021-01-28 6:02 ` Darrick J. Wong
2021-01-28 6:02 ` [PATCH 06/11] xfs: flush eof/cowblocks if we can't reserve quota for file blocks Darrick J. Wong
` (5 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:02 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, Brian Foster, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
Change the signature of xfs_blockgc_free_quota in preparation for the
next few patches. Callers can now pass EOF_FLAGS into the function to
control scan parameters; and the function will now pass back any
corruption errors seen while scanning, though for our retry loops we'll
just try again unconditionally.
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_file.c | 10 +++++-----
fs/xfs/xfs_icache.c | 26 +++++++++++++++++---------
fs/xfs/xfs_icache.h | 2 +-
3 files changed, 23 insertions(+), 15 deletions(-)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index d69e5abcc1b4..3be0b1d81325 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -743,14 +743,14 @@ xfs_file_buffered_write(
* metadata space. This reduces the chances that the eofblocks scan
* waits on dirty mappings. Since xfs_flush_inodes() is serialized, this
* also behaves as a filter to prevent too many eofblocks scans from
- * running at the same time.
+ * running at the same time. Use a synchronous scan to increase the
+ * effectiveness of the scan.
*/
if (ret == -EDQUOT && !cleared_space) {
xfs_iunlock(ip, iolock);
- cleared_space = xfs_blockgc_free_quota(ip);
- if (cleared_space)
- goto write_retry;
- iolock = 0;
+ xfs_blockgc_free_quota(ip, XFS_EOF_FLAGS_SYNC);
+ cleared_space = true;
+ goto write_retry;
} else if (ret == -ENOSPC && !cleared_space) {
struct xfs_eofblocks eofb = {0};
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index aba901d5637b..4a074aa12b52 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1650,20 +1650,26 @@ xfs_start_block_reaping(
* with multiple quotas, we don't know exactly which quota caused an allocation
* failure. We make a best effort by including each quota under low free space
* conditions (less than 1% free space) in the scan.
+ *
+ * Callers must not hold any inode's ILOCK. If requesting a synchronous scan
+ * (XFS_EOF_FLAGS_SYNC), the caller also must not hold any inode's IOLOCK or
+ * MMAPLOCK.
*/
-bool
+int
xfs_blockgc_free_quota(
- struct xfs_inode *ip)
+ struct xfs_inode *ip,
+ unsigned int eof_flags)
{
struct xfs_eofblocks eofb = {0};
struct xfs_dquot *dq;
bool do_work = false;
+ int error;
/*
- * Run a sync scan to increase effectiveness and use the union filter to
- * cover all applicable quotas in a single scan.
+ * Run a scan to free blocks using the union filter to cover all
+ * applicable quotas in a single scan.
*/
- eofb.eof_flags = XFS_EOF_FLAGS_UNION | XFS_EOF_FLAGS_SYNC;
+ eofb.eof_flags = XFS_EOF_FLAGS_UNION | eof_flags;
if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
dq = xfs_inode_dquot(ip, XFS_DQTYPE_USER);
@@ -1693,9 +1699,11 @@ xfs_blockgc_free_quota(
}
if (!do_work)
- return false;
+ return 0;
- xfs_icache_free_eofblocks(ip->i_mount, &eofb);
- xfs_icache_free_cowblocks(ip->i_mount, &eofb);
- return true;
+ error = xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+ if (error)
+ return error;
+
+ return xfs_icache_free_cowblocks(ip->i_mount, &eofb);
}
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index 21b726a05b0d..d64ea8f5c589 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -54,7 +54,7 @@ long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan);
void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
-bool xfs_blockgc_free_quota(struct xfs_inode *ip);
+int xfs_blockgc_free_quota(struct xfs_inode *ip, unsigned int eof_flags);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 06/11] xfs: flush eof/cowblocks if we can't reserve quota for file blocks
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (4 preceding siblings ...)
2021-01-28 6:02 ` [PATCH 05/11] xfs: pass flags and return gc errors from xfs_blockgc_free_quota Darrick J. Wong
@ 2021-01-28 6:02 ` Darrick J. Wong
2021-01-28 9:26 ` Christoph Hellwig
2021-01-28 6:03 ` [PATCH 07/11] xfs: flush eof/cowblocks if we can't reserve quota for inode creation Darrick J. Wong
` (4 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:02 UTC (permalink / raw)
To: djwong; +Cc: linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
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>
---
fs/xfs/xfs_quota.h | 15 ++++++--
fs/xfs/xfs_reflink.c | 7 +++-
fs/xfs/xfs_trans.c | 9 ++++-
fs/xfs/xfs_trans_dquot.c | 87 +++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 107 insertions(+), 11 deletions(-)
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index c3a5b48f5860..dcdc3cd0361a 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -82,7 +82,9 @@ extern void xfs_trans_mod_dquot_byino(struct xfs_trans *, struct xfs_inode *,
extern void xfs_trans_apply_dquot_deltas(struct xfs_trans *);
extern void xfs_trans_unreserve_and_mod_dquots(struct xfs_trans *);
int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp, struct xfs_inode *ip,
- int64_t dblocks, int64_t rblocks, bool force);
+ int64_t dblocks, int64_t rblocks, bool force,
+ unsigned int *retry);
+void xfs_trans_cancel_qretry_nblks(struct xfs_trans *tp, struct xfs_inode *ip);
extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
struct xfs_mount *, struct xfs_dquot *,
struct xfs_dquot *, struct xfs_dquot *, int64_t, long, uint);
@@ -114,7 +116,8 @@ extern void xfs_qm_unmount_quotas(struct xfs_mount *);
static inline int
xfs_quota_reserve_blkres(struct xfs_inode *ip, int64_t dblocks)
{
- return xfs_trans_reserve_quota_nblks(NULL, ip, dblocks, 0, false);
+ return xfs_trans_reserve_quota_nblks(NULL, ip, dblocks, 0, false,
+ NULL);
}
#else
static inline int
@@ -134,7 +137,7 @@ xfs_qm_vop_dqalloc(struct xfs_inode *ip, kuid_t kuid, kgid_t kgid,
#define xfs_trans_unreserve_and_mod_dquots(tp)
static inline int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp,
struct xfs_inode *ip, int64_t dblocks, int64_t rblocks,
- bool force)
+ bool force, unsigned int *retry)
{
return 0;
}
@@ -159,6 +162,12 @@ xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_dquot *udqp,
return 0;
}
+static inline void
+xfs_trans_cancel_qretry_nblks(struct xfs_trans *tp, struct xfs_inode *ip)
+{
+ ASSERT(0);
+}
+
#define xfs_qm_vop_create_dqattach(tp, ip, u, g, p)
#define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index c6296fd1512f..068e70d7a3bc 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -1093,10 +1093,15 @@ 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,
- dmap->br_blockcount, 0, false);
+ dmap->br_blockcount, 0, false, NULL);
if (error)
goto out_cancel;
}
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index bfb8b2a1594f..3d1f745b721a 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -1046,8 +1046,10 @@ xfs_trans_alloc_inode(
{
struct xfs_trans *tp;
struct xfs_mount *mp = ip->i_mount;
+ unsigned int qretry = 0;
int error;
+retry:
error = xfs_trans_alloc(mp, resv, dblocks,
rblocks / mp->m_sb.sb_rextsize,
force ? XFS_TRANS_RESERVE : 0, &tp);
@@ -1064,9 +1066,14 @@ xfs_trans_alloc_inode(
goto out_cancel;
}
- error = xfs_trans_reserve_quota_nblks(tp, ip, dblocks, rblocks, force);
+ error = xfs_trans_reserve_quota_nblks(tp, ip, dblocks, rblocks, force,
+ &qretry);
if (error)
goto out_cancel;
+ if (qretry) {
+ xfs_trans_cancel_qretry_nblks(tp, ip);
+ goto retry;
+ }
*tpp = tp;
return 0;
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 73ef5994d09d..229e2a20889f 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -16,6 +16,7 @@
#include "xfs_quota.h"
#include "xfs_qm.h"
#include "xfs_trace.h"
+#include "xfs_icache.h"
STATIC void xfs_trans_alloc_dqinfo(xfs_trans_t *);
@@ -770,11 +771,67 @@ xfs_trans_reserve_quota_bydquots(
return error;
}
+/*
+ * Decide what to do after an attempt to reserve some quota. This will set
+ * the retry and error parameters as needed, and returns true if the quota
+ * reservation succeeded.
+ */
+static inline bool
+reservation_success(
+ unsigned int qflag,
+ unsigned int *retry,
+ int *error)
+{
+ /*
+ * The caller is not interested in retries. Return whether or not we
+ * got an error code.
+ */
+ if (retry == NULL)
+ return *error == 0;
+
+ if (*error == -EDQUOT || *error == -ENOSPC) {
+ /*
+ * There isn't enough quota left to allow the reservation.
+ *
+ * If none of the retry bits are set, this is the first time
+ * that we failed a quota reservation. Zero the error code and
+ * set the appropriate quota flag in *retry to encourage the
+ * xfs_trans_reserve_quota_nblks caller to clear some space and
+ * call back.
+ *
+ * If any of the retry bits are set, this means that we failed
+ * the quota reservation once before, tried to free some quota,
+ * and have now failed the second quota reservation attempt.
+ * Pass the error back to the caller; we're done.
+ */
+ if (!(*retry))
+ *error = 0;
+
+ *retry |= qflag;
+ return false;
+ }
+
+ /* A non-quota error occurred; there is no retry. */
+ if (error)
+ return false;
+
+ /*
+ * The reservation succeeded. Clear the quota flag from the retry
+ * state because there is nothing to retry.
+ */
+ *retry &= ~qflag;
+ return true;
+}
/*
- * Lock the dquot and change the reservation if we can.
- * This doesn't change the actual usage, just the reservation.
- * The inode sent in is locked.
+ * Lock the dquot and change the reservation if we can. This doesn't change
+ * the actual usage, just the reservation. The caller must hold ILOCK_EXCL on
+ * the inode. If @retry is not a NULL pointer, the caller must ensure that
+ * *retry is set to 0 before the first time this function is called.
+ *
+ * If the quota reservation fails because we hit a quota limit (and retry is
+ * not a NULL pointer, and *retry is zero), this function will set *retry to
+ * nonzero and return zero.
*/
int
xfs_trans_reserve_quota_nblks(
@@ -782,7 +839,8 @@ xfs_trans_reserve_quota_nblks(
struct xfs_inode *ip,
int64_t dblocks,
int64_t rblocks,
- bool force)
+ bool force,
+ unsigned int *retry)
{
struct xfs_mount *mp = ip->i_mount;
unsigned int qflags = 0;
@@ -801,14 +859,14 @@ xfs_trans_reserve_quota_nblks(
error = xfs_trans_reserve_quota_bydquots(tp, mp, ip->i_udquot,
ip->i_gdquot, ip->i_pdquot, dblocks, 0,
XFS_QMOPT_RES_REGBLKS | qflags);
- if (error)
+ if (!reservation_success(XFS_QMOPT_RES_REGBLKS, retry, &error))
return error;
/* Do the same but for realtime blocks. */
error = xfs_trans_reserve_quota_bydquots(tp, mp, ip->i_udquot,
ip->i_gdquot, ip->i_pdquot, rblocks, 0,
XFS_QMOPT_RES_RTBLKS | qflags);
- if (error) {
+ if (!reservation_success(XFS_QMOPT_RES_RTBLKS, retry, &error)) {
xfs_trans_reserve_quota_bydquots(tp, mp, ip->i_udquot,
ip->i_gdquot, ip->i_pdquot, -dblocks, 0,
XFS_QMOPT_RES_REGBLKS);
@@ -818,6 +876,23 @@ xfs_trans_reserve_quota_nblks(
return 0;
}
+/*
+ * Cancel a transaction and try to clear some space so that we can reserve some
+ * quota. The caller must hold the ILOCK; when this function returns, the
+ * transaction will be cancelled and the ILOCK will have been released.
+ */
+void
+xfs_trans_cancel_qretry_nblks(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
+{
+ ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+
+ xfs_trans_cancel(tp);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_blockgc_free_quota(ip, 0);
+}
+
/* Change the quota reservations for an inode creation activity. */
int
xfs_trans_reserve_quota_icreate(
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 07/11] xfs: flush eof/cowblocks if we can't reserve quota for inode creation
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (5 preceding siblings ...)
2021-01-28 6:02 ` [PATCH 06/11] xfs: flush eof/cowblocks if we can't reserve quota for file blocks Darrick J. Wong
@ 2021-01-28 6:03 ` Darrick J. Wong
2021-01-28 6:03 ` [PATCH 08/11] xfs: flush eof/cowblocks if we can't reserve quota for chown Darrick J. Wong
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:03 UTC (permalink / raw)
To: djwong; +Cc: linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
If an inode creation 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>
---
fs/xfs/xfs_icache.c | 73 +++++++++++++++++++++++++++-------------------
fs/xfs/xfs_icache.h | 2 +
fs/xfs/xfs_quota.h | 18 ++++++++++-
fs/xfs/xfs_trans.c | 9 +++++-
fs/xfs/xfs_trans_dquot.c | 36 +++++++++++++++++++++--
5 files changed, 102 insertions(+), 36 deletions(-)
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 4a074aa12b52..cd369dd48818 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1646,64 +1646,77 @@ xfs_start_block_reaping(
}
/*
- * Run cow/eofblocks scans on the quotas applicable to the inode. For inodes
- * with multiple quotas, we don't know exactly which quota caused an allocation
- * failure. We make a best effort by including each quota under low free space
- * conditions (less than 1% free space) in the scan.
+ * Run cow/eofblocks scans on the supplied dquots. We don't know exactly which
+ * quota caused an allocation failure, so we make a best effort by including
+ * each quota under low free space conditions (less than 1% free space) in the
+ * scan.
*
* Callers must not hold any inode's ILOCK. If requesting a synchronous scan
* (XFS_EOF_FLAGS_SYNC), the caller also must not hold any inode's IOLOCK or
* MMAPLOCK.
*/
int
-xfs_blockgc_free_quota(
- struct xfs_inode *ip,
+xfs_blockgc_free_dquots(
+ struct xfs_dquot *udqp,
+ struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp,
unsigned int eof_flags)
{
struct xfs_eofblocks eofb = {0};
- struct xfs_dquot *dq;
+ struct xfs_mount *mp = NULL;
bool do_work = false;
int error;
+ if (!udqp && !gdqp && !pdqp)
+ return 0;
+ if (udqp)
+ mp = udqp->q_mount;
+ if (!mp && gdqp)
+ mp = gdqp->q_mount;
+ if (!mp && pdqp)
+ mp = pdqp->q_mount;
+
/*
* Run a scan to free blocks using the union filter to cover all
* applicable quotas in a single scan.
*/
eofb.eof_flags = XFS_EOF_FLAGS_UNION | eof_flags;
- if (XFS_IS_UQUOTA_ENFORCED(ip->i_mount)) {
- dq = xfs_inode_dquot(ip, XFS_DQTYPE_USER);
- if (dq && xfs_dquot_lowsp(dq)) {
- eofb.eof_uid = VFS_I(ip)->i_uid;
- eofb.eof_flags |= XFS_EOF_FLAGS_UID;
- do_work = true;
- }
+ if (XFS_IS_UQUOTA_ENFORCED(mp) && udqp && xfs_dquot_lowsp(udqp)) {
+ eofb.eof_uid = make_kuid(mp->m_super->s_user_ns, udqp->q_id);
+ eofb.eof_flags |= XFS_EOF_FLAGS_UID;
+ do_work = true;
}
- if (XFS_IS_GQUOTA_ENFORCED(ip->i_mount)) {
- dq = xfs_inode_dquot(ip, XFS_DQTYPE_GROUP);
- if (dq && xfs_dquot_lowsp(dq)) {
- eofb.eof_gid = VFS_I(ip)->i_gid;
- eofb.eof_flags |= XFS_EOF_FLAGS_GID;
- do_work = true;
- }
+ if (XFS_IS_UQUOTA_ENFORCED(mp) && gdqp && xfs_dquot_lowsp(gdqp)) {
+ eofb.eof_gid = make_kgid(mp->m_super->s_user_ns, gdqp->q_id);
+ eofb.eof_flags |= XFS_EOF_FLAGS_GID;
+ do_work = true;
}
- if (XFS_IS_PQUOTA_ENFORCED(ip->i_mount)) {
- dq = xfs_inode_dquot(ip, XFS_DQTYPE_PROJ);
- if (dq && xfs_dquot_lowsp(dq)) {
- eofb.eof_prid = ip->i_d.di_projid;
- eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
- do_work = true;
- }
+ if (XFS_IS_PQUOTA_ENFORCED(mp) && pdqp && xfs_dquot_lowsp(pdqp)) {
+ eofb.eof_prid = pdqp->q_id;
+ eofb.eof_flags |= XFS_EOF_FLAGS_PRID;
+ do_work = true;
}
if (!do_work)
return 0;
- error = xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+ error = xfs_icache_free_eofblocks(mp, &eofb);
if (error)
return error;
- return xfs_icache_free_cowblocks(ip->i_mount, &eofb);
+ return xfs_icache_free_cowblocks(mp, &eofb);
+}
+
+/* Run cow/eofblocks scans on the quotas attached to the inode. */
+int
+xfs_blockgc_free_quota(
+ struct xfs_inode *ip,
+ unsigned int eof_flags)
+{
+ return xfs_blockgc_free_dquots(xfs_inode_dquot(ip, XFS_DQTYPE_USER),
+ xfs_inode_dquot(ip, XFS_DQTYPE_GROUP),
+ xfs_inode_dquot(ip, XFS_DQTYPE_PROJ), eof_flags);
}
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index d64ea8f5c589..5f520de637f6 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -54,6 +54,8 @@ long xfs_reclaim_inodes_nr(struct xfs_mount *mp, int nr_to_scan);
void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
+int xfs_blockgc_free_dquots(struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp, unsigned int eof_flags);
int xfs_blockgc_free_quota(struct xfs_inode *ip, unsigned int eof_flags);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index dcdc3cd0361a..7d87a1244256 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -90,7 +90,10 @@ extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
struct xfs_dquot *, struct xfs_dquot *, int64_t, long, uint);
int xfs_trans_reserve_quota_icreate(struct xfs_trans *tp,
struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
- struct xfs_dquot *pdqp, int64_t dblocks);
+ struct xfs_dquot *pdqp, int64_t dblocks, unsigned int *retry);
+void xfs_trans_cancel_qretry_icreate(struct xfs_trans *tp,
+ struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp);
extern int xfs_qm_vop_dqalloc(struct xfs_inode *, kuid_t, kgid_t,
prid_t, uint, struct xfs_dquot **, struct xfs_dquot **,
@@ -157,7 +160,8 @@ xfs_quota_reserve_blkres(struct xfs_inode *ip, int64_t dblocks)
static inline int
xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_dquot *udqp,
- struct xfs_dquot *gdqp, struct xfs_dquot *pdqp, int64_t dblocks)
+ struct xfs_dquot *gdqp, struct xfs_dquot *pdqp, int64_t dblocks,
+ unsigned int *retry)
{
return 0;
}
@@ -168,6 +172,16 @@ xfs_trans_cancel_qretry_nblks(struct xfs_trans *tp, struct xfs_inode *ip)
ASSERT(0);
}
+static inline void
+xfs_trans_cancel_qretry_icreate(
+ struct xfs_trans *tp,
+ struct xfs_dquot *udqp,
+ struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp)
+{
+ ASSERT(0);
+}
+
#define xfs_qm_vop_create_dqattach(tp, ip, u, g, p)
#define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 3d1f745b721a..7d4823cbd7c0 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -1099,17 +1099,24 @@ xfs_trans_alloc_icreate(
struct xfs_trans **tpp)
{
struct xfs_trans *tp;
+ unsigned int qretry = 0;
int error;
+retry:
error = xfs_trans_alloc(mp, resv, dblocks, 0, 0, &tp);
if (error)
return error;
- error = xfs_trans_reserve_quota_icreate(tp, udqp, gdqp, pdqp, dblocks);
+ error = xfs_trans_reserve_quota_icreate(tp, udqp, gdqp, pdqp, dblocks,
+ &qretry);
if (error) {
xfs_trans_cancel(tp);
return error;
}
+ if (qretry) {
+ xfs_trans_cancel_qretry_icreate(tp, udqp, gdqp, pdqp);
+ goto retry;
+ }
*tpp = tp;
return 0;
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 229e2a20889f..2358c9e35be8 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -893,22 +893,52 @@ xfs_trans_cancel_qretry_nblks(
xfs_blockgc_free_quota(ip, 0);
}
-/* Change the quota reservations for an inode creation activity. */
+/*
+ * Change the quota reservations for an inode creation activity. This doesn't
+ * change the actual usage, just the reservation. If @retry is not a NULL
+ * pointer, the caller must ensure that *retry is set to zero before the first
+ * time this function is called.
+ *
+ * If the quota reservation fails because we hit a quota limit (and retry is
+ * not a NULL pointer, and *retry is zero), this function will set *retry to
+ * nonzero and return zero.
+ */
int
xfs_trans_reserve_quota_icreate(
struct xfs_trans *tp,
struct xfs_dquot *udqp,
struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp,
- int64_t dblocks)
+ int64_t dblocks,
+ unsigned int *retry)
{
struct xfs_mount *mp = tp->t_mountp;
+ int error;
if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
return 0;
- return xfs_trans_reserve_quota_bydquots(tp, mp, udqp, gdqp, pdqp,
+ error = xfs_trans_reserve_quota_bydquots(tp, mp, udqp, gdqp, pdqp,
dblocks, 1, XFS_QMOPT_RES_REGBLKS);
+ if (!reservation_success(XFS_QMOPT_RES_REGBLKS, retry, &error))
+ return error;
+
+ return 0;
+}
+
+/*
+ * Cancel a transaction and try to clear some space so that we can reserve some
+ * quota. When this function returns, the transaction will be cancelled.
+ */
+void
+xfs_trans_cancel_qretry_icreate(
+ struct xfs_trans *tp,
+ struct xfs_dquot *udqp,
+ struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp)
+{
+ xfs_trans_cancel(tp);
+ xfs_blockgc_free_dquots(udqp, gdqp, pdqp, 0);
}
/*
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 08/11] xfs: flush eof/cowblocks if we can't reserve quota for chown
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (6 preceding siblings ...)
2021-01-28 6:03 ` [PATCH 07/11] xfs: flush eof/cowblocks if we can't reserve quota for inode creation Darrick J. Wong
@ 2021-01-28 6:03 ` Darrick J. Wong
2021-01-28 6:03 ` [PATCH 09/11] xfs: add a tracepoint for blockgc scans Darrick J. Wong
` (2 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:03 UTC (permalink / raw)
To: djwong; +Cc: linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
If a file user, group, or project change 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.
Note that the retry loop is open-coded here because there are only two
places in the codebase where we ever change [ugp]id; and the setattr and
setxattr code is already tricky enough.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_ioctl.c | 9 ++++++++-
fs/xfs/xfs_iops.c | 10 +++++++++-
fs/xfs/xfs_quota.h | 18 ++++++++++++++++--
fs/xfs/xfs_trans_dquot.c | 46 ++++++++++++++++++++++++++++++++++++++++++----
4 files changed, 75 insertions(+), 8 deletions(-)
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 73cfee8007a8..6adfc8541d61 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1436,6 +1436,7 @@ xfs_ioctl_setattr(
struct xfs_trans *tp;
struct xfs_dquot *pdqp = NULL;
struct xfs_dquot *olddquot = NULL;
+ unsigned int quota_retry = 0;
int code;
trace_xfs_ioctl_setattr(ip);
@@ -1462,6 +1463,7 @@ xfs_ioctl_setattr(
xfs_ioctl_setattr_prepare_dax(ip, fa);
+retry:
tp = xfs_ioctl_setattr_get_trans(ip);
if (IS_ERR(tp)) {
code = PTR_ERR(tp);
@@ -1471,9 +1473,14 @@ xfs_ioctl_setattr(
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
ip->i_d.di_projid != fa->fsx_projid) {
code = xfs_trans_reserve_quota_chown(tp, ip, NULL, NULL, pdqp,
- capable(CAP_FOWNER));
+ capable(CAP_FOWNER), "a_retry);
if (code) /* out of quota */
goto error_trans_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry_chown(tp, ip, NULL, NULL,
+ pdqp);
+ goto retry;
+ }
}
xfs_fill_fsxattr(ip, false, &old_fa);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 51c877ce90bc..9453c984d715 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -660,6 +660,7 @@ xfs_setattr_nonsize(
kgid_t gid = GLOBAL_ROOT_GID, igid = GLOBAL_ROOT_GID;
struct xfs_dquot *udqp = NULL, *gdqp = NULL;
struct xfs_dquot *olddquot1 = NULL, *olddquot2 = NULL;
+ unsigned int quota_retry = 0;
ASSERT((mask & ATTR_SIZE) == 0);
@@ -700,6 +701,7 @@ xfs_setattr_nonsize(
return error;
}
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
if (error)
goto out_dqrele;
@@ -731,9 +733,15 @@ xfs_setattr_nonsize(
(XFS_IS_GQUOTA_ON(mp) && !gid_eq(igid, gid)))) {
ASSERT(tp);
error = xfs_trans_reserve_quota_chown(tp, ip, udqp,
- gdqp, NULL, capable(CAP_FOWNER));
+ gdqp, NULL, capable(CAP_FOWNER),
+ "a_retry);
if (error) /* out of quota */
goto out_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry_chown(tp, ip, udqp,
+ gdqp, NULL);
+ goto retry;
+ }
}
/*
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index 7d87a1244256..4f45e7feb841 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -105,7 +105,10 @@ extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *,
struct xfs_inode *, struct xfs_dquot **, struct xfs_dquot *);
int xfs_trans_reserve_quota_chown(struct xfs_trans *tp, struct xfs_inode *ip,
struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
- struct xfs_dquot *pdqp, bool force);
+ struct xfs_dquot *pdqp, bool force, unsigned int *retry);
+void xfs_trans_cancel_qretry_chown(struct xfs_trans *tp, struct xfs_inode *ip,
+ struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp);
extern int xfs_qm_dqattach(struct xfs_inode *);
extern int xfs_qm_dqattach_locked(struct xfs_inode *ip, bool doalloc);
extern void xfs_qm_dqdetach(struct xfs_inode *);
@@ -182,13 +185,24 @@ xfs_trans_cancel_qretry_icreate(
ASSERT(0);
}
+static inline void
+xfs_trans_cancel_qretry_chown(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ struct xfs_dquot *udqp,
+ struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp)
+{
+ ASSERT(0);
+}
+
#define xfs_qm_vop_create_dqattach(tp, ip, u, g, p)
#define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
static inline int
xfs_trans_reserve_quota_chown(struct xfs_trans *tp, struct xfs_inode *ip,
struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
- struct xfs_dquot *pdqp, bool force)
+ struct xfs_dquot *pdqp, bool force, unsigned int *retry)
{
return 0;
}
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 2358c9e35be8..7518424ee8b4 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -942,7 +942,15 @@ xfs_trans_cancel_qretry_icreate(
}
/*
- * Quota reservations for setattr(AT_UID|AT_GID|AT_PROJID).
+ * Chagnge quota reservations for setattr(AT_UID|AT_GID|AT_PROJID). This
+ * doesn't change the actual usage, just the reservation. The caller must hold
+ * ILOCK_EXCL on the inode. If @retry is not a NULL pointer, the caller must
+ * ensure that *retry is set to zero before the first time this function is
+ * called.
+ *
+ * If the quota reservation fails because we hit a quota limit (and retry is
+ * not a NULL pointer, and *retry is zero), this function will set *retry to
+ * nonzero and return zero.
*/
int
xfs_trans_reserve_quota_chown(
@@ -951,7 +959,8 @@ xfs_trans_reserve_quota_chown(
struct xfs_dquot *udqp,
struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp,
- bool force)
+ bool force,
+ unsigned int *retry)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_dquot *udq_unres = NULL; /* old dquots */
@@ -1011,7 +1020,7 @@ xfs_trans_reserve_quota_chown(
gdq_delblks, pdq_delblks, ip->i_d.di_nblocks, 1,
qflags);
if (error)
- return error;
+ goto err;
/*
* Do the delayed blks reservations/unreservations now. Since, these
@@ -1029,13 +1038,42 @@ xfs_trans_reserve_quota_chown(
udq_delblks, gdq_delblks, pdq_delblks,
(xfs_qcnt_t)delblks, 0, qflags);
if (error)
- return error;
+ goto err;
xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, udq_unres,
gdq_unres, pdq_unres, -((xfs_qcnt_t)delblks),
0, qflags);
}
return 0;
+err:
+ /*
+ * Handle all quota reservation failures in the same place because we
+ * don't want the success predicate to clear REGBLKS from the retry
+ * flags after we _reserve_quota for the ondisk blocks but before we
+ * _reserve_quota for the delalloc blocks. If we were called with a
+ * nonzero *retry, that means we failed to get the quota reservation
+ * once before and do not want to schedule a retry on a second error.
+ */
+ reservation_success(XFS_QMOPT_RES_REGBLKS, retry, &error);
+ return error;
+}
+
+/*
+ * Cancel a transaction and try to clear some space so that we can reserve some
+ * quota. When this function returns, the transaction will be cancelled and
+ * ILOCK_EXCL will no longer be held.
+ */
+void
+xfs_trans_cancel_qretry_chown(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ struct xfs_dquot *udqp,
+ struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp)
+{
+ xfs_trans_cancel(tp);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_blockgc_free_dquots(udqp, gdqp, pdqp, 0);
}
/*
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 09/11] xfs: add a tracepoint for blockgc scans
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (7 preceding siblings ...)
2021-01-28 6:03 ` [PATCH 08/11] xfs: flush eof/cowblocks if we can't reserve quota for chown Darrick J. Wong
@ 2021-01-28 6:03 ` Darrick J. Wong
2021-01-28 6:03 ` [PATCH 10/11] xfs: refactor xfs_icache_free_{eof,cow}blocks call sites Darrick J. Wong
2021-01-28 6:03 ` [PATCH 11/11] xfs: flush speculative space allocations when we run out of space Darrick J. Wong
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:03 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
Add some tracepoints so that we can observe when the speculative
preallocation garbage collector runs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/xfs_ioctl.c | 2 ++
fs/xfs/xfs_trace.c | 1 +
fs/xfs/xfs_trace.h | 41 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 44 insertions(+)
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 6adfc8541d61..cc6ddc6d22a0 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -2355,6 +2355,8 @@ xfs_file_ioctl(
if (error)
return error;
+ trace_xfs_ioc_free_eofblocks(mp, &keofb, _RET_IP_);
+
sb_start_write(mp->m_super);
error = xfs_icache_free_eofblocks(mp, &keofb);
sb_end_write(mp->m_super);
diff --git a/fs/xfs/xfs_trace.c b/fs/xfs/xfs_trace.c
index 120398a37c2a..9b8d703dc9fd 100644
--- a/fs/xfs/xfs_trace.c
+++ b/fs/xfs/xfs_trace.c
@@ -29,6 +29,7 @@
#include "xfs_filestream.h"
#include "xfs_fsmap.h"
#include "xfs_btree_staging.h"
+#include "xfs_icache.h"
/*
* We include this last to have the helpers above available for the trace
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 407c3a5208ab..38649e3341cb 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -37,6 +37,7 @@ struct xfs_trans_res;
struct xfs_inobt_rec_incore;
union xfs_btree_ptr;
struct xfs_dqtrx;
+struct xfs_eofblocks;
#define XFS_ATTR_FILTER_FLAGS \
{ XFS_ATTR_ROOT, "ROOT" }, \
@@ -3888,6 +3889,46 @@ DEFINE_EVENT(xfs_timestamp_range_class, name, \
DEFINE_TIMESTAMP_RANGE_EVENT(xfs_inode_timestamp_range);
DEFINE_TIMESTAMP_RANGE_EVENT(xfs_quota_expiry_range);
+DECLARE_EVENT_CLASS(xfs_eofblocks_class,
+ TP_PROTO(struct xfs_mount *mp, struct xfs_eofblocks *eofb,
+ unsigned long caller_ip),
+ TP_ARGS(mp, eofb, caller_ip),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(__u32, flags)
+ __field(uint32_t, uid)
+ __field(uint32_t, gid)
+ __field(prid_t, prid)
+ __field(__u64, min_file_size)
+ __field(unsigned long, caller_ip)
+ ),
+ TP_fast_assign(
+ __entry->dev = mp->m_super->s_dev;
+ __entry->flags = eofb ? eofb->eof_flags : 0;
+ __entry->uid = eofb ? from_kuid(mp->m_super->s_user_ns,
+ eofb->eof_uid) : 0;
+ __entry->gid = eofb ? from_kgid(mp->m_super->s_user_ns,
+ eofb->eof_gid) : 0;
+ __entry->prid = eofb ? eofb->eof_prid : 0;
+ __entry->min_file_size = eofb ? eofb->eof_min_file_size : 0;
+ __entry->caller_ip = caller_ip;
+ ),
+ TP_printk("dev %d:%d flags 0x%x uid %u gid %u prid %u minsize %llu caller %pS",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->flags,
+ __entry->uid,
+ __entry->gid,
+ __entry->prid,
+ __entry->min_file_size,
+ (char *)__entry->caller_ip)
+);
+#define DEFINE_EOFBLOCKS_EVENT(name) \
+DEFINE_EVENT(xfs_eofblocks_class, name, \
+ TP_PROTO(struct xfs_mount *mp, struct xfs_eofblocks *eofb, \
+ unsigned long caller_ip), \
+ TP_ARGS(mp, eofb, caller_ip))
+DEFINE_EOFBLOCKS_EVENT(xfs_ioc_free_eofblocks);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 10/11] xfs: refactor xfs_icache_free_{eof,cow}blocks call sites
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (8 preceding siblings ...)
2021-01-28 6:03 ` [PATCH 09/11] xfs: add a tracepoint for blockgc scans Darrick J. Wong
@ 2021-01-28 6:03 ` Darrick J. Wong
2021-01-28 6:03 ` [PATCH 11/11] xfs: flush speculative space allocations when we run out of space Darrick J. Wong
10 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:03 UTC (permalink / raw)
To: djwong; +Cc: Christoph Hellwig, linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
In anticipation of more restructuring of the eof/cowblocks gc code,
refactor calling of those two functions into a single internal helper
function, then present a new standard interface to purge speculative
block preallocations and start shifting higher level code to use that.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/xfs_file.c | 3 +--
fs/xfs/xfs_icache.c | 39 +++++++++++++++++++++++++++++++++------
fs/xfs/xfs_icache.h | 1 +
fs/xfs/xfs_trace.h | 1 +
4 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 3be0b1d81325..dc91973c0b4f 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -759,8 +759,7 @@ xfs_file_buffered_write(
xfs_iunlock(ip, iolock);
eofb.eof_flags = XFS_EOF_FLAGS_SYNC;
- xfs_icache_free_eofblocks(ip->i_mount, &eofb);
- xfs_icache_free_cowblocks(ip->i_mount, &eofb);
+ xfs_blockgc_free_space(ip->i_mount, &eofb);
goto write_retry;
}
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index cd369dd48818..97c15fcdd6f7 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1645,6 +1645,38 @@ xfs_start_block_reaping(
xfs_queue_cowblocks(mp);
}
+/* Scan all incore inodes for block preallocations that we can remove. */
+static inline int
+xfs_blockgc_scan(
+ struct xfs_mount *mp,
+ struct xfs_eofblocks *eofb)
+{
+ int error;
+
+ error = xfs_icache_free_eofblocks(mp, eofb);
+ if (error)
+ return error;
+
+ error = xfs_icache_free_cowblocks(mp, eofb);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+/*
+ * Try to free space in the filesystem by purging eofblocks and cowblocks.
+ */
+int
+xfs_blockgc_free_space(
+ struct xfs_mount *mp,
+ struct xfs_eofblocks *eofb)
+{
+ trace_xfs_blockgc_free_space(mp, eofb, _RET_IP_);
+
+ return xfs_blockgc_scan(mp, eofb);
+}
+
/*
* Run cow/eofblocks scans on the supplied dquots. We don't know exactly which
* quota caused an allocation failure, so we make a best effort by including
@@ -1665,7 +1697,6 @@ xfs_blockgc_free_dquots(
struct xfs_eofblocks eofb = {0};
struct xfs_mount *mp = NULL;
bool do_work = false;
- int error;
if (!udqp && !gdqp && !pdqp)
return 0;
@@ -1703,11 +1734,7 @@ xfs_blockgc_free_dquots(
if (!do_work)
return 0;
- error = xfs_icache_free_eofblocks(mp, &eofb);
- if (error)
- return error;
-
- return xfs_icache_free_cowblocks(mp, &eofb);
+ return xfs_blockgc_free_space(mp, &eofb);
}
/* Run cow/eofblocks scans on the quotas attached to the inode. */
diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h
index 5f520de637f6..583c132ae0fb 100644
--- a/fs/xfs/xfs_icache.h
+++ b/fs/xfs/xfs_icache.h
@@ -57,6 +57,7 @@ void xfs_inode_set_reclaim_tag(struct xfs_inode *ip);
int xfs_blockgc_free_dquots(struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp, unsigned int eof_flags);
int xfs_blockgc_free_quota(struct xfs_inode *ip, unsigned int eof_flags);
+int xfs_blockgc_free_space(struct xfs_mount *mp, struct xfs_eofblocks *eofb);
void xfs_inode_set_eofblocks_tag(struct xfs_inode *ip);
void xfs_inode_clear_eofblocks_tag(struct xfs_inode *ip);
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 38649e3341cb..27929c6ca43a 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -3928,6 +3928,7 @@ DEFINE_EVENT(xfs_eofblocks_class, name, \
unsigned long caller_ip), \
TP_ARGS(mp, eofb, caller_ip))
DEFINE_EOFBLOCKS_EVENT(xfs_ioc_free_eofblocks);
+DEFINE_EOFBLOCKS_EVENT(xfs_blockgc_free_space);
#endif /* _TRACE_XFS_H */
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 11/11] xfs: flush speculative space allocations when we run out of space
2021-01-28 6:02 [PATCHSET v5 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
` (9 preceding siblings ...)
2021-01-28 6:03 ` [PATCH 10/11] xfs: refactor xfs_icache_free_{eof,cow}blocks call sites Darrick J. Wong
@ 2021-01-28 6:03 ` Darrick J. Wong
2021-01-28 9:26 ` Christoph Hellwig
10 siblings, 1 reply; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-28 6:03 UTC (permalink / raw)
To: djwong; +Cc: linux-xfs, hch, david, bfoster
From: Darrick J. Wong <djwong@kernel.org>
If a fs modification (creation, file write, reflink, etc.) is unable to
reserve enough space 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>
---
fs/xfs/xfs_trans.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 7d4823cbd7c0..eb39baddff35 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -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;
@@ -288,6 +289,18 @@ xfs_trans_alloc(
tp->t_firstblock = NULLFSBLOCK;
error = xfs_trans_reserve(tp, resp, blocks, rtextents);
+ if (error == -ENOSPC) {
+ /*
+ * We weren't able to reserve enough space for the transaction.
+ * Flush the other speculative space allocations to free space.
+ * Do not perform a synchronous scan because callers can hold
+ * other locks.
+ */
+ error = xfs_blockgc_free_space(mp, NULL);
+ if (error)
+ return error;
+ error = xfs_trans_reserve(tp, resp, blocks, rtextents);
+ }
if (error) {
xfs_trans_cancel(tp);
return error;
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 11/11] xfs: flush speculative space allocations when we run out of space
2021-01-28 6:03 ` [PATCH 11/11] xfs: flush speculative space allocations when we run out of space Darrick J. Wong
@ 2021-01-28 9:26 ` Christoph Hellwig
0 siblings, 0 replies; 16+ messages in thread
From: Christoph Hellwig @ 2021-01-28 9:26 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs, hch, david, bfoster
On Wed, Jan 27, 2021 at 10:03:23PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> If a fs modification (creation, file write, reflink, etc.) is unable to
> reserve enough space 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>
Looks good,
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 08/11] xfs: flush eof/cowblocks if we can't reserve quota for chown
2021-01-18 22:11 [PATCHSET v3 00/11] xfs: try harder to reclaim space when we run out Darrick J. Wong
@ 2021-01-18 22:12 ` Darrick J. Wong
0 siblings, 0 replies; 16+ messages in thread
From: Darrick J. Wong @ 2021-01-18 22:12 UTC (permalink / raw)
To: djwong; +Cc: linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If a file user, group, or project change 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>
---
fs/xfs/xfs_ioctl.c | 16 ++++++++++++----
fs/xfs/xfs_iops.c | 20 ++++++++++++--------
fs/xfs/xfs_qm.c | 36 +++++++++++++++++++++++++++---------
fs/xfs/xfs_quota.h | 8 ++++----
4 files changed, 55 insertions(+), 25 deletions(-)
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 3fbd98f61ea5..70d9637b7806 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1436,6 +1436,7 @@ xfs_ioctl_setattr(
struct xfs_trans *tp;
struct xfs_dquot *pdqp = NULL;
struct xfs_dquot *olddquot = NULL;
+ bool quota_retry = false;
int code;
trace_xfs_ioctl_setattr(ip);
@@ -1462,6 +1463,7 @@ xfs_ioctl_setattr(
xfs_ioctl_setattr_prepare_dax(ip, fa);
+retry:
tp = xfs_ioctl_setattr_get_trans(ip);
if (IS_ERR(tp)) {
code = PTR_ERR(tp);
@@ -1470,10 +1472,16 @@ xfs_ioctl_setattr(
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
ip->i_d.di_projid != fa->fsx_projid) {
- code = xfs_qm_vop_chown_reserve(tp, ip, NULL, NULL, pdqp,
- capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0);
- if (code) /* out of quota */
- goto error_trans_cancel;
+ unsigned int flags = 0;
+
+ if (capable(CAP_FOWNER))
+ flags |= XFS_QMOPT_FORCE_RES;
+ code = xfs_qm_vop_chown_reserve(&tp, ip, NULL, NULL, pdqp,
+ flags, "a_retry);
+ if (code)
+ goto error_free_dquots;
+ if (quota_retry)
+ goto retry;
}
xfs_fill_fsxattr(ip, false, &old_fa);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 67c8dc9de8aa..3cc6c4aa01c3 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -660,6 +660,7 @@ xfs_setattr_nonsize(
kgid_t gid = GLOBAL_ROOT_GID, igid = GLOBAL_ROOT_GID;
struct xfs_dquot *udqp = NULL, *gdqp = NULL;
struct xfs_dquot *olddquot1 = NULL, *olddquot2 = NULL;
+ bool quota_retry = false;
ASSERT((mask & ATTR_SIZE) == 0);
@@ -700,6 +701,7 @@ xfs_setattr_nonsize(
return error;
}
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
if (error)
goto out_dqrele;
@@ -729,12 +731,17 @@ xfs_setattr_nonsize(
if (XFS_IS_QUOTA_RUNNING(mp) &&
((XFS_IS_UQUOTA_ON(mp) && !uid_eq(iuid, uid)) ||
(XFS_IS_GQUOTA_ON(mp) && !gid_eq(igid, gid)))) {
+ unsigned int flags = 0;
+
+ if (capable(CAP_FOWNER))
+ flags |= XFS_QMOPT_FORCE_RES;
ASSERT(tp);
- error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
- NULL, capable(CAP_FOWNER) ?
- XFS_QMOPT_FORCE_RES : 0);
- if (error) /* out of quota */
- goto out_cancel;
+ error = xfs_qm_vop_chown_reserve(&tp, ip, udqp, gdqp,
+ NULL, flags, "a_retry);
+ if (error)
+ goto out_dqrele;
+ if (quota_retry)
+ goto retry;
}
/*
@@ -814,9 +821,6 @@ xfs_setattr_nonsize(
return 0;
-out_cancel:
- xfs_trans_cancel(tp);
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
out_dqrele:
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index c134eb4aeaa8..4c095526aaed 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1795,27 +1795,29 @@ xfs_qm_vop_chown(
}
/*
- * Quota reservations for setattr(AT_UID|AT_GID|AT_PROJID).
+ * Quota reservations for setattr(AT_UID|AT_GID|AT_PROJID). This function has
+ * the same return behavior as xfs_trans_reserve_quota_nblks.
*/
int
xfs_qm_vop_chown_reserve(
- struct xfs_trans *tp,
+ struct xfs_trans **tpp,
struct xfs_inode *ip,
struct xfs_dquot *udqp,
struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp,
- uint flags)
+ unsigned int flags,
+ bool *retry)
{
struct xfs_mount *mp = ip->i_mount;
uint64_t delblks;
unsigned int blkflags;
- struct xfs_dquot *udq_unres = NULL;
+ struct xfs_dquot *udq_unres = NULL; /* old dquots */
struct xfs_dquot *gdq_unres = NULL;
struct xfs_dquot *pdq_unres = NULL;
- struct xfs_dquot *udq_delblks = NULL;
+ struct xfs_dquot *udq_delblks = NULL; /* new dquots */
struct xfs_dquot *gdq_delblks = NULL;
struct xfs_dquot *pdq_delblks = NULL;
- int error;
+ int error, err2;
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
@@ -1856,11 +1858,11 @@ xfs_qm_vop_chown_reserve(
}
}
- error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
+ error = xfs_trans_reserve_quota_bydquots(*tpp, ip->i_mount,
udq_delblks, gdq_delblks, pdq_delblks,
ip->i_d.di_nblocks, 1, flags | blkflags);
if (error)
- return error;
+ goto err;
/*
* Do the delayed blks reservations/unreservations now. Since, these
@@ -1878,13 +1880,29 @@ xfs_qm_vop_chown_reserve(
udq_delblks, gdq_delblks, pdq_delblks,
(xfs_qcnt_t)delblks, 0, flags | blkflags);
if (error)
- return error;
+ goto err;
xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
udq_unres, gdq_unres, pdq_unres,
-((xfs_qcnt_t)delblks), 0, blkflags);
}
return 0;
+err:
+ xfs_trans_cancel(*tpp);
+ *tpp = NULL;
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ /* We only allow one retry for EDQUOT/ENOSPC. */
+ if (*retry || (error != -EDQUOT && error != -ENOSPC)) {
+ *retry = false;
+ return error;
+ }
+
+ /* Try to free some quota in the new dquots. */
+ err2 = xfs_blockgc_free_dquots(udq_delblks, gdq_delblks, pdq_delblks,
+ 0, retry);
+ if (err2)
+ return err2;
+ return *retry ? 0 : error;
}
int
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index ec09b38a9687..2ec57a65b1db 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -100,9 +100,9 @@ extern void xfs_qm_vop_create_dqattach(struct xfs_trans *, struct xfs_inode *,
extern int xfs_qm_vop_rename_dqattach(struct xfs_inode **);
extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *,
struct xfs_inode *, struct xfs_dquot **, struct xfs_dquot *);
-extern int xfs_qm_vop_chown_reserve(struct xfs_trans *, struct xfs_inode *,
- struct xfs_dquot *, struct xfs_dquot *,
- struct xfs_dquot *, uint);
+int xfs_qm_vop_chown_reserve(struct xfs_trans **tpp, struct xfs_inode *ip,
+ struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
+ struct xfs_dquot *pdqp, unsigned int flags, bool *retry);
extern int xfs_qm_dqattach(struct xfs_inode *);
extern int xfs_qm_dqattach_locked(struct xfs_inode *ip, bool doalloc);
extern void xfs_qm_dqdetach(struct xfs_inode *);
@@ -157,7 +157,7 @@ xfs_trans_reserve_quota_icreate(struct xfs_trans **tpp, struct xfs_inode *dp,
#define xfs_qm_vop_create_dqattach(tp, ip, u, g, p)
#define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
-#define xfs_qm_vop_chown_reserve(tp, ip, u, g, p, fl) (0)
+#define xfs_qm_vop_chown_reserve(tpp, ip, u, g, p, fl, retry) (0)
#define xfs_qm_dqattach(ip) (0)
#define xfs_qm_dqattach_locked(ip, fl) (0)
#define xfs_qm_dqdetach(ip)
^ permalink raw reply related [flat|nested] 16+ messages in thread