@@ -1270,3 +1270,48 @@ xfs_qm_dqiterate(
return error;
}
+
+/* Update dquot pending-inactivation counters. */
+STATIC void
+xfs_dquot_adjust(
+ struct xfs_dquot *dqp,
+ int direction,
+ int64_t inodes,
+ int64_t dblocks,
+ int64_t rblocks)
+{
+ xfs_dqlock(dqp);
+ dqp->q_ina_total += direction;
+ dqp->q_ina_icount += inodes;
+ dqp->q_ina_bcount += dblocks;
+ dqp->q_ina_rtbcount += rblocks;
+ xfs_dqunlock(dqp);
+}
+
+/* Update pending-inactivation counters for all dquots attach to inode. */
+void
+xfs_qm_iadjust(
+ struct xfs_inode *ip,
+ int direction,
+ int64_t inodes,
+ int64_t dblocks,
+ int64_t rblocks)
+{
+ struct xfs_mount *mp = ip->i_mount;
+
+ if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp) ||
+ xfs_is_quota_inode(&mp->m_sb, ip->i_ino))
+ return;
+
+ if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot)
+ xfs_dquot_adjust(ip->i_udquot, direction, inodes, dblocks,
+ rblocks);
+
+ if (XFS_IS_GQUOTA_ON(mp) && ip->i_gdquot)
+ xfs_dquot_adjust(ip->i_gdquot, direction, inodes, dblocks,
+ rblocks);
+
+ if (XFS_IS_PQUOTA_ON(mp) && ip->i_pdquot)
+ xfs_dquot_adjust(ip->i_pdquot, direction, inodes, dblocks,
+ rblocks);
+}
@@ -45,6 +45,10 @@ typedef struct xfs_dquot {
xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
xfs_qcnt_t q_res_icount; /* total inos allocd+reserved */
xfs_qcnt_t q_res_rtbcount;/* total realtime blks used+reserved */
+ uint64_t q_ina_total; /* inactive inodes attached here */
+ xfs_qcnt_t q_ina_bcount; /* inactive regular nblks used+reserved */
+ xfs_qcnt_t q_ina_icount; /* inactive inos allocd+reserved */
+ xfs_qcnt_t q_ina_rtbcount;/* inactive realtime blks used+reserved */
xfs_qcnt_t q_prealloc_lo_wmark;/* prealloc throttle wmark */
xfs_qcnt_t q_prealloc_hi_wmark;/* prealloc disabled wmark */
int64_t q_low_space[XFS_QLOWSP_MAX];
@@ -41,6 +41,8 @@
#include "xfs_bmap_btree.h"
#include "xfs_reflink.h"
#include "xfs_dir2_priv.h"
+#include "xfs_dquot_item.h"
+#include "xfs_dquot.h"
kmem_zone_t *xfs_inode_zone;
@@ -1836,6 +1838,8 @@ xfs_inode_iadjust(
percpu_counter_add(&mp->m_iinactive, inodes);
percpu_counter_add(&mp->m_dinactive, dblocks);
percpu_counter_add(&mp->m_rinactive, rblocks);
+
+ xfs_qm_iadjust(ip, direction, inodes, dblocks, rblocks);
}
/*
@@ -426,6 +426,19 @@ xfs_qm_dquot_isolate(
if (!xfs_dqlock_nowait(dqp))
goto out_miss_busy;
+ /*
+ * An inode is on the inactive list waiting to release its resources,
+ * so remove this dquot from the freelist and try again. We detached
+ * the dquot from the NEEDS_INACTIVE inode so that quotaoff won't
+ * deadlock on inactive inodes holding dquots.
+ */
+ if (dqp->q_ina_total > 0) {
+ xfs_dqunlock(dqp);
+ trace_xfs_dqreclaim_inactive(dqp);
+ list_lru_isolate(lru, &dqp->q_lru);
+ return LRU_REMOVED;
+ }
+
/*
* This dquot has acquired a reference in the meantime remove it from
* the freelist and try again.
@@ -626,8 +626,8 @@ xfs_qm_scall_getquota_fill_qc(
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_softlimit));
dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit);
dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit);
- dst->d_space = XFS_FSB_TO_B(mp, dqp->q_res_bcount);
- dst->d_ino_count = dqp->q_res_icount;
+ dst->d_space = XFS_FSB_TO_B(mp, dqp->q_res_bcount - dqp->q_ina_bcount);
+ dst->d_ino_count = dqp->q_res_icount - dqp->q_ina_icount;
dst->d_spc_timer = be32_to_cpu(dqp->q_core.d_btimer);
dst->d_ino_timer = be32_to_cpu(dqp->q_core.d_itimer);
dst->d_ino_warns = be16_to_cpu(dqp->q_core.d_iwarns);
@@ -636,7 +636,8 @@ xfs_qm_scall_getquota_fill_qc(
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit));
dst->d_rt_spc_softlimit =
XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit));
- dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_res_rtbcount);
+ dst->d_rt_space =
+ XFS_FSB_TO_B(mp, dqp->q_res_rtbcount - dqp->q_ina_rtbcount);
dst->d_rt_spc_timer = be32_to_cpu(dqp->q_core.d_rtbtimer);
dst->d_rt_spc_warns = be16_to_cpu(dqp->q_core.d_rtbwarns);
@@ -103,7 +103,8 @@ extern int xfs_qm_newmount(struct xfs_mount *, uint *, uint *);
extern void xfs_qm_mount_quotas(struct xfs_mount *);
extern void xfs_qm_unmount(struct xfs_mount *);
extern void xfs_qm_unmount_quotas(struct xfs_mount *);
-
+extern void xfs_qm_iadjust(struct xfs_inode *ip, int direction, int64_t inodes,
+ int64_t dblocks, int64_t rblocks);
#else
static inline int
xfs_qm_vop_dqalloc(struct xfs_inode *ip, xfs_dqid_t uid, xfs_dqid_t gid,
@@ -145,6 +146,7 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
#define xfs_qm_mount_quotas(mp)
#define xfs_qm_unmount(mp)
#define xfs_qm_unmount_quotas(mp)
+#define xfs_qm_iadjust(ip, dir, inodes, dblocks, rblocks)
#endif /* CONFIG_XFS_QUOTA */
#define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \
@@ -901,6 +901,7 @@ DEFINE_EVENT(xfs_dquot_class, name, \
TP_PROTO(struct xfs_dquot *dqp), \
TP_ARGS(dqp))
DEFINE_DQUOT_EVENT(xfs_dqadjust);
+DEFINE_DQUOT_EVENT(xfs_dqreclaim_inactive);
DEFINE_DQUOT_EVENT(xfs_dqreclaim_want);
DEFINE_DQUOT_EVENT(xfs_dqreclaim_dirty);
DEFINE_DQUOT_EVENT(xfs_dqreclaim_busy);