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