When project quota gets exceeded xfs_iomap_write_delay() ends up flushing
inodes because ENOSPC gets returned from xfs_bmapi_delay() instead of EDQUOT.
This makes handling of writes over project quota rather slow as a simple test
program shows:
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
for (i = 0; i < 50000; i++)
pwrite(fd, buf, 4096, i*4096);
Writing 200 MB like this into a directory with 100 MB project quota takes
around 6 minutes while it takes about 2 seconds with this patch applied. This
actually happens in a real world load when nfs pushes data into a directory
which is over project quota.
Fix the problem by replacing XFS_QMOPT_ENOSPC flag with XFS_QMOPT_EPDQUOT.
That makes xfs_trans_reserve_quota_bydquots() return new error EPDQUOT when
project quota is exceeded. xfs_bmapi_delay() then uses this flag so that
xfs_iomap_write_delay() can distinguish real ENOSPC (requiring flushing)
from exceeded project quota (not requiring flushing).
As a side effect this patch fixes inconsistency where e.g. xfs_create()
returned EDQUOT even when project quota was exceeded.
Signed-off-by: Jan Kara <jack@xxxxxxx>
---
fs/xfs/xfs_bmap.c | 3 ++-
fs/xfs/xfs_iomap.c | 8 ++++++--
fs/xfs/xfs_linux.h | 1 +
fs/xfs/xfs_qm.c | 13 +++++--------
fs/xfs/xfs_quota.h | 2 +-
fs/xfs/xfs_trans_dquot.c | 18 +++++++++---------
6 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 848ffa7..027398f 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -4471,7 +4471,8 @@ xfs_bmapi_reserve_delalloc(
* allocated blocks already inside this loop.
*/
error = xfs_trans_reserve_quota_nblks(NULL, ip, (long)alen, 0,
- rt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+ XFS_QMOPT_EPDQUOT |
+ (rt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS));
if (error)
return error;
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 973dff6..86e8016 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -428,6 +428,7 @@ retry:
case 0:
case ENOSPC:
case EDQUOT:
+ case EPDQUOT:
break;
default:
return XFS_ERROR(error);
@@ -441,8 +442,11 @@ retry:
*/
if (nimaps == 0) {
trace_xfs_delalloc_enospc(ip, offset, count);
- if (flushed)
- return XFS_ERROR(error ? error : ENOSPC);
+ if (flushed) {
+ if (error == 0 || error == EPDQUOT)
+ error = ENOSPC;
+ return XFS_ERROR(error);
+ }
if (error == ENOSPC) {
xfs_iunlock(ip, XFS_ILOCK_EXCL);
diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h
index 828662f..31368df 100644
--- a/fs/xfs/xfs_linux.h
+++ b/fs/xfs/xfs_linux.h
@@ -145,6 +145,7 @@
#define ENOATTR ENODATA /* Attribute not found */
#define EWRONGFS EINVAL /* Mount with wrong filesystem type */
#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */
+#define EPDQUOT EXFULL /* Project quota exceeded */
#define SYNCHRONIZE() barrier()
#define __return_address __builtin_return_address(0)
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 2e86fa0..99ace03 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1790,7 +1790,7 @@ xfs_qm_vop_chown_reserve(
uint flags)
{
xfs_mount_t *mp = ip->i_mount;
- uint delblks, blkflags, prjflags = 0;
+ uint delblks, blkflags;
xfs_dquot_t *unresudq, *unresgdq, *delblksudq, *delblksgdq;
int error;
@@ -1817,11 +1817,8 @@ xfs_qm_vop_chown_reserve(
}
}
if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) {
- if (XFS_IS_PQUOTA_ON(ip->i_mount) &&
- xfs_get_projid(ip) != be32_to_cpu(gdqp->q_core.d_id))
- prjflags = XFS_QMOPT_ENOSPC;
-
- if (prjflags ||
+ if ((XFS_IS_PQUOTA_ON(ip->i_mount) &&
+ xfs_get_projid(ip) != be32_to_cpu(gdqp->q_core.d_id)) ||
(XFS_IS_GQUOTA_ON(ip->i_mount) &&
ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) {
delblksgdq = gdqp;
@@ -1834,7 +1831,7 @@ xfs_qm_vop_chown_reserve(
if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
delblksudq, delblksgdq, ip->i_d.di_nblocks, 1,
- flags | blkflags | prjflags)))
+ flags | blkflags)))
return (error);
/*
@@ -1851,7 +1848,7 @@ xfs_qm_vop_chown_reserve(
ASSERT(unresudq || unresgdq);
if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0,
- flags | blkflags | prjflags)))
+ flags | blkflags)))
return (error);
xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0,
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
index b50ec5b..264b455 100644
--- a/fs/xfs/xfs_quota.h
+++ b/fs/xfs/xfs_quota.h
@@ -203,7 +203,7 @@ typedef struct xfs_qoff_logformat {
#define XFS_QMOPT_DOWARN 0x0000400 /* increase warning cnt if needed */
#define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot if damaged */
#define XFS_QMOPT_GQUOTA 0x0002000 /* group dquot requested */
-#define XFS_QMOPT_ENOSPC 0x0004000 /* enospc instead of edquot (prj) */
+#define XFS_QMOPT_EPDQUOT 0x0004000 /* return EPDQUOT when project quota exceeded */
/*
* flags to xfs_trans_mod_dquot to indicate which field needs to be
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index 0c7fa54..27acce3 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -728,8 +728,6 @@ xfs_trans_dqresv(
error_return:
xfs_dqunlock(dqp);
- if (flags & XFS_QMOPT_ENOSPC)
- return ENOSPC;
return EDQUOT;
}
@@ -741,7 +739,6 @@ error_return:
* approach.
*
* flags = XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
- * XFS_QMOPT_ENOSPC returns ENOSPC not EDQUOT. Used by pquota.
* XFS_TRANS_DQ_RES_BLKS reserves regular disk blocks
* XFS_TRANS_DQ_RES_RTBLKS reserves realtime disk blocks
* dquots are unlocked on return, if they were not locked by caller.
@@ -767,8 +764,7 @@ xfs_trans_reserve_quota_bydquots(
ASSERT(flags & XFS_QMOPT_RESBLK_MASK);
if (udqp) {
- error = xfs_trans_dqresv(tp, mp, udqp, nblks, ninos,
- (flags & ~XFS_QMOPT_ENOSPC));
+ error = xfs_trans_dqresv(tp, mp, udqp, nblks, ninos, flags);
if (error)
return error;
resvd = 1;
@@ -785,6 +781,12 @@ xfs_trans_reserve_quota_bydquots(
xfs_trans_dqresv(tp, mp, udqp,
-nblks, -ninos, flags);
}
+ if (error == EDQUOT && XFS_IS_PQUOTA_ON(mp)) {
+ if (flags & XFS_QMOPT_EPDQUOT)
+ error = EPDQUOT;
+ else
+ error = ENOSPC;
+ }
return error;
}
}
@@ -813,16 +815,14 @@ xfs_trans_reserve_quota_nblks(
if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
return 0;
- if (XFS_IS_PQUOTA_ON(mp))
- flags |= XFS_QMOPT_ENOSPC;
ASSERT(ip->i_ino != mp->m_sb.sb_uquotino);
ASSERT(ip->i_ino != mp->m_sb.sb_gquotino);
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
- ASSERT((flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
+ ASSERT((flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_EPDQUOT)) ==
XFS_TRANS_DQ_RES_RTBLKS ||
- (flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) ==
+ (flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_EPDQUOT)) ==
XFS_TRANS_DQ_RES_BLKS);
/*
--
1.7.1
_______________________________________________
xfs mailing list
xfs@xxxxxxxxxxx
http://oss.sgi.com/mailman/listinfo/xfs