@@ -395,6 +395,7 @@ xfs_attr_set(
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
+ bool quota_retry = false;
int error, local;
int rmt_blks = 0;
unsigned int total;
@@ -458,6 +459,7 @@ xfs_attr_set(
* Root fork attributes can use reserved data blocks for this
* operation if necessary
*/
+retry:
error = xfs_trans_alloc(mp, &tres, total, 0,
rsvd ? XFS_TRANS_RESERVE : 0, &args->trans);
if (error)
@@ -479,9 +481,14 @@ xfs_attr_set(
if (rsvd)
quota_flags |= XFS_QMOPT_FORCE_RES;
error = xfs_trans_reserve_quota_nblks(args->trans, dp,
- args->total, 0, quota_flags);
+ args->total, 0, quota_flags, "a_retry);
if (error)
goto out_trans_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(args->trans, dp);
+ args->trans = NULL;
+ goto retry;
+ }
error = xfs_has_attr(args);
if (error == -EEXIST && (args->attr_flags & XATTR_CREATE))
@@ -1070,6 +1070,7 @@ xfs_bmap_add_attrfork(
int blks; /* space reservation */
int version = 1; /* superblock attr version */
int logflags; /* logging flags */
+ bool quota_retry = false;
int error; /* error return value */
ASSERT(XFS_IFORK_Q(ip) == 0);
@@ -1079,6 +1080,7 @@ xfs_bmap_add_attrfork(
blks = XFS_ADDAFORK_SPACE_RES(mp);
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_addafork, blks, 0,
rsvd ? XFS_TRANS_RESERVE : 0, &tp);
if (error)
@@ -1087,9 +1089,13 @@ xfs_bmap_add_attrfork(
xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_quota_nblks(tp, ip, blks, 0, rsvd ?
XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
- XFS_QMOPT_RES_REGBLKS);
+ XFS_QMOPT_RES_REGBLKS, "a_retry);
if (error)
goto trans_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
if (XFS_IFORK_Q(ip))
goto trans_cancel;
@@ -761,6 +761,7 @@ xfs_alloc_file_space(
*/
while (allocatesize_fsb && !error) {
xfs_fileoff_t s, e;
+ bool quota_retry = false;
/*
* Determine space reservations for data/realtime.
@@ -803,6 +804,7 @@ xfs_alloc_file_space(
/*
* Allocate and setup the transaction.
*/
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks,
resrtextents, 0, &tp);
@@ -817,10 +819,14 @@ xfs_alloc_file_space(
break;
}
xfs_ilock(ip, XFS_ILOCK_EXCL);
- error = xfs_trans_reserve_quota_nblks(tp, ip, qblocks,
- 0, quota_flag);
+ error = xfs_trans_reserve_quota_nblks(tp, ip, qblocks, 0,
+ quota_flag, "a_retry);
if (error)
goto error1;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT);
@@ -858,7 +864,6 @@ xfs_alloc_file_space(
error0: /* unlock inode, unreserve quota blocks, cancel trans */
xfs_trans_unreserve_quota_nblks(tp, ip, (long)qblocks, 0, quota_flag);
-
error1: /* Just cancel transaction */
xfs_trans_cancel(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -875,8 +880,10 @@ xfs_unmap_extent(
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
uint resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
+ bool quota_retry = false;
int error;
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
if (error) {
ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp));
@@ -885,9 +892,13 @@ xfs_unmap_extent(
xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,
- XFS_QMOPT_RES_REGBLKS);
+ XFS_QMOPT_RES_REGBLKS, "a_retry);
if (error)
goto out_trans_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
xfs_trans_ijoin(tp, ip, 0);
@@ -27,7 +27,7 @@
#include "xfs_dquot_item.h"
#include "xfs_dquot.h"
#include "xfs_reflink.h"
-
+#include "xfs_icache.h"
#define XFS_ALLOC_ALIGN(mp, off) \
(((off) >> mp->m_allocsize_log) << mp->m_allocsize_log)
@@ -197,6 +197,7 @@ xfs_iomap_write_direct(
int quota_flag;
uint qblocks, resblks;
unsigned int resrtextents = 0;
+ bool quota_retry = false;
int error;
int bmapi_flags = XFS_BMAPI_PREALLOC;
uint tflags = 0;
@@ -239,6 +240,7 @@ xfs_iomap_write_direct(
resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0) << 1;
}
}
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, resrtextents,
tflags, &tp);
if (error)
@@ -246,9 +248,14 @@ xfs_iomap_write_direct(
xfs_ilock(ip, XFS_ILOCK_EXCL);
- error = xfs_trans_reserve_quota_nblks(tp, ip, qblocks, 0, quota_flag);
+ error = xfs_trans_reserve_quota_nblks(tp, ip, qblocks, 0, quota_flag,
+ "a_retry);
if (error)
goto out_trans_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT);
@@ -544,6 +551,8 @@ xfs_iomap_write_unwritten(
return error;
do {
+ bool quota_retry = false;
+
/*
* Set up a transaction to convert the range of extents
* from unwritten to real. Do allocations in a loop until
@@ -553,6 +562,7 @@ xfs_iomap_write_unwritten(
* here as we might be asked to write out the same inode that we
* complete here and might deadlock on the iolock.
*/
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0,
XFS_TRANS_RESERVE, &tp);
if (error)
@@ -562,9 +572,14 @@ xfs_iomap_write_unwritten(
xfs_trans_ijoin(tp, ip, 0);
error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,
- XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES);
+ XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES,
+ "a_retry);
if (error)
goto error_on_bmapi_transaction;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
error = xfs_iext_count_may_overflow(ip, XFS_DATA_FORK,
XFS_IEXT_WRITE_UNWRITTEN_CNT);
@@ -81,14 +81,16 @@ extern void xfs_trans_mod_dquot_byino(struct xfs_trans *, struct xfs_inode *,
uint, int64_t);
extern void xfs_trans_apply_dquot_deltas(struct xfs_trans *);
extern void xfs_trans_unreserve_and_mod_dquots(struct xfs_trans *);
-extern int xfs_trans_reserve_quota_nblks(struct xfs_trans *,
- struct xfs_inode *, int64_t, long, uint);
+int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp, struct xfs_inode *ip,
+ int64_t nblocks, long ninos, unsigned int flags,
+ bool *retry);
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);
int xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_inode *dp,
struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp, int64_t nblks);
+int xfs_trans_cancel_qretry(struct xfs_trans *tp, struct xfs_inode *ip);
extern int xfs_qm_vop_dqalloc(struct xfs_inode *, kuid_t, kgid_t,
prid_t, uint, struct xfs_dquot **, struct xfs_dquot **,
@@ -115,7 +117,8 @@ static inline int
xfs_quota_reserve_blkres(struct xfs_inode *ip, int64_t nblks, bool isrt)
{
return xfs_trans_reserve_quota_nblks(NULL, ip, nblks, 0,
- isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+ isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS,
+ NULL);
}
#else
static inline int
@@ -134,7 +137,8 @@ xfs_qm_vop_dqalloc(struct xfs_inode *ip, kuid_t kuid, kgid_t kgid,
#define xfs_trans_apply_dquot_deltas(tp)
#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 nblks, long ninos, uint flags)
+ struct xfs_inode *ip, int64_t nblks, long ninos,
+ unsigned int flags, bool *retry)
{
return 0;
}
@@ -160,6 +164,13 @@ xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_inode *dp,
return 0;
}
+static inline int
+xfs_trans_cancel_qretry(struct xfs_trans *tp, struct xfs_inode *ip)
+{
+ ASSERT(0);
+ return 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)
@@ -179,7 +190,8 @@ static inline int
xfs_trans_unreserve_quota_nblks(struct xfs_trans *tp, struct xfs_inode *ip,
int64_t nblks, long ninos, unsigned int flags)
{
- return xfs_trans_reserve_quota_nblks(tp, ip, -nblks, -ninos, flags);
+ return xfs_trans_reserve_quota_nblks(tp, ip, -nblks, -ninos, flags,
+ NULL);
}
static inline int
@@ -355,6 +355,7 @@ xfs_reflink_allocate_cow(
xfs_filblks_t count_fsb = imap->br_blockcount;
struct xfs_trans *tp;
int nimaps, error = 0;
+ bool quota_retry = false;
bool found;
xfs_filblks_t resaligned;
xfs_extlen_t resblks = 0;
@@ -376,6 +377,7 @@ xfs_reflink_allocate_cow(
resblks = XFS_DIOSTRAT_SPACE_RES(mp, resaligned);
xfs_iunlock(ip, *lockmode);
+retry:
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
*lockmode = XFS_ILOCK_EXCL;
xfs_ilock(ip, *lockmode);
@@ -399,9 +401,13 @@ xfs_reflink_allocate_cow(
}
error = xfs_trans_reserve_quota_nblks(tp, ip, resblks, 0,
- XFS_QMOPT_RES_REGBLKS);
+ XFS_QMOPT_RES_REGBLKS, "a_retry);
if (error)
goto out_trans_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
xfs_trans_ijoin(tp, ip, 0);
@@ -1006,10 +1012,12 @@ xfs_reflink_remap_extent(
unsigned int resblks;
bool smap_real;
bool dmap_written = xfs_bmap_is_written_extent(dmap);
+ bool quota_retry = false;
int iext_delta = 0;
int nimaps;
int error;
+retry:
/* Start a rolling transaction to switch the mappings */
resblks = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
@@ -1095,9 +1103,13 @@ xfs_reflink_remap_extent(
qres += dmap->br_blockcount;
if (qres > 0) {
error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0,
- XFS_QMOPT_RES_REGBLKS);
+ XFS_QMOPT_RES_REGBLKS, "a_retry);
if (error)
goto out_cancel;
+ if (quota_retry) {
+ xfs_trans_cancel_qretry(tp, ip);
+ goto retry;
+ }
}
if (smap_real)
@@ -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,15 @@ xfs_trans_reserve_quota_bydquots(
return error;
}
-
/*
- * 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 false 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 false), this function will set *retry to
+ * true and return zero.
*/
int
xfs_trans_reserve_quota_nblks(
@@ -782,9 +787,11 @@ xfs_trans_reserve_quota_nblks(
struct xfs_inode *ip,
int64_t nblks,
long ninos,
- uint flags)
+ unsigned int flags,
+ bool *retry)
{
struct xfs_mount *mp = ip->i_mount;
+ int error;
if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
return 0;
@@ -795,13 +802,37 @@ xfs_trans_reserve_quota_nblks(
ASSERT((flags & ~(XFS_QMOPT_FORCE_RES)) == XFS_TRANS_DQ_RES_RTBLKS ||
(flags & ~(XFS_QMOPT_FORCE_RES)) == XFS_TRANS_DQ_RES_BLKS);
- /*
- * Reserve nblks against these dquots, with trans as the mediator.
- */
- return xfs_trans_reserve_quota_bydquots(tp, mp,
- ip->i_udquot, ip->i_gdquot,
- ip->i_pdquot,
- nblks, ninos, flags);
+ /* Reserve nblks against these dquots, with trans as the mediator. */
+ error = xfs_trans_reserve_quota_bydquots(tp, mp, ip->i_udquot,
+ ip->i_gdquot, ip->i_pdquot, nblks, ninos, flags);
+ if (retry == NULL)
+ return error;
+ /* We only allow one retry for EDQUOT/ENOSPC. */
+ if (*retry || (error != -EDQUOT && error != -ENOSPC)) {
+ *retry = false;
+ return error;
+ }
+
+ *retry = true;
+ 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.
+ */
+int
+xfs_trans_cancel_qretry(
+ 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);
+
+ return xfs_blockgc_free_quota(ip, 0);
}
/* Change the quota reservations for an inode creation activity. */