From: Darrick J. Wong <darrick.wong@xxxxxxxxxx> Enable the bigtime feature for quota timers. We decrease the accuracy of the timers to ~4s in exchange for being able to set timers up to the bigtime maximum. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- fs/xfs/libxfs/xfs_dquot_buf.c | 72 ++++++++++++++++++++++++++++++++++++++-- fs/xfs/libxfs/xfs_format.h | 22 ++++++++++++ fs/xfs/libxfs/xfs_quota_defs.h | 11 ++++-- fs/xfs/scrub/quota.c | 5 +++ fs/xfs/xfs_dquot.c | 71 +++++++++++++++++++++++++++++++-------- fs/xfs/xfs_ondisk.h | 6 +++ fs/xfs/xfs_qm.c | 13 ++++--- fs/xfs/xfs_qm.h | 8 ++-- fs/xfs/xfs_qm_syscalls.c | 19 ++++++----- 9 files changed, 186 insertions(+), 41 deletions(-) diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c index 72e0fcfef580..2b5d51a6d64b 100644 --- a/fs/xfs/libxfs/xfs_dquot_buf.c +++ b/fs/xfs/libxfs/xfs_dquot_buf.c @@ -40,6 +40,8 @@ xfs_dquot_verify( xfs_dqid_t id, uint type) /* used only during quotacheck */ { + uint8_t dtype; + /* * We can encounter an uninitialized dquot buffer for 2 reasons: * 1. If we crash while deleting the quotainode(s), and those blks got @@ -60,11 +62,22 @@ xfs_dquot_verify( if (ddq->d_version != XFS_DQUOT_VERSION) return __this_address; - if (type && ddq->d_flags != type) + dtype = ddq->d_flags & XFS_DQ_ALLTYPES; + if (type && dtype != type) + return __this_address; + if (dtype != XFS_DQ_USER && + dtype != XFS_DQ_PROJ && + dtype != XFS_DQ_GROUP) + return __this_address; + + if (ddq->d_flags & ~(XFS_DQ_ALLTYPES | XFS_DQ_BIGTIME)) return __this_address; - if (ddq->d_flags != XFS_DQ_USER && - ddq->d_flags != XFS_DQ_PROJ && - ddq->d_flags != XFS_DQ_GROUP) + + if ((ddq->d_flags & XFS_DQ_BIGTIME) && + !xfs_sb_version_hasbigtime(&mp->m_sb)) + return __this_address; + + if ((ddq->d_flags & XFS_DQ_BIGTIME) && !ddq->d_id) return __this_address; if (id != -1 && id != be32_to_cpu(ddq->d_id)) @@ -290,17 +303,68 @@ const struct xfs_buf_ops xfs_dquot_buf_ra_ops = { void xfs_dquot_from_disk_timestamp( + struct xfs_disk_dquot *ddq, struct timespec64 *tv, __be32 dtimer) { tv->tv_nsec = 0; + + /* Zero always means zero, regardless of encoding. */ + if (!dtimer) { + tv->tv_sec = 0; + return; + } + + if (ddq->d_flags & XFS_DQ_BIGTIME) { + uint64_t t; + + t = be32_to_cpu(dtimer); + tv->tv_sec = t << XFS_DQ_BIGTIME_SHIFT; + return; + } + tv->tv_sec = be32_to_cpu(dtimer); } void xfs_dquot_to_disk_timestamp( + struct xfs_disk_dquot *ddq, __be32 *dtimer, const struct timespec64 *tv) { + /* Zero always means zero, regardless of encoding. */ + if ((ddq->d_flags & XFS_DQ_BIGTIME) && tv->tv_sec != 0) { + uint64_t t = tv->tv_sec; + + /* + * Round the end of the grace period up to the nearest bigtime + * interval that we support, to give users the most time to fix + * the problems. + */ + t = roundup_64(t, 1U << XFS_DQ_BIGTIME_SHIFT); + *dtimer = cpu_to_be32(t >> XFS_DQ_BIGTIME_SHIFT); + return; + } + *dtimer = cpu_to_be32(tv->tv_sec); } + +/* + * Convert the dquot to bigtime format incore so that we'll write out the new + * values the next time we flush the dquot to disk. Skip this for d_id == 0 + * because that dquot stores the grace period intervals. Returns true if we + * upgraded the format, false otherwise. + */ +bool +xfs_dquot_add_bigtime( + struct xfs_mount *mp, + struct xfs_disk_dquot *ddq) +{ + if (xfs_sb_version_hasbigtime(&mp->m_sb) && ddq->d_id && + !(ddq->d_flags & XFS_DQ_BIGTIME)) { + ddq->d_flags |= XFS_DQ_BIGTIME; + return true; + } + + return false; +} diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index 9b127e4f4077..bab0b23720bb 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -1248,6 +1248,28 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev) #define XFS_DQ_GRACE_MIN ((int64_t)0) #define XFS_DQ_GRACE_MAX ((int64_t)U32_MAX) +/* + * When bigtime is enabled, we trade a few bits of precision to expand the + * expiration timeout range to match that of big inode timestamps. The grace + * periods stored in dquot 0 are not shifted, since they record an interval, + * not a timestamp. + */ +#define XFS_DQ_BIGTIME_SHIFT (2) + +/* + * Smallest possible quota expiration with big timestamps, which is + * Jan 1 00:00:01 UTC 1970. + */ +#define XFS_DQ_BIGTIMEOUT_MIN (XFS_DQ_TIMEOUT_MIN) + +/* + * Largest supported quota expiration with traditional timestamps, which is + * the largest bigtime inode timestamp, or Jul 2 20:20:25 UTC 2486. The field + * is large enough that it's possible to fit expirations up to 2514, but we + * want to keep the maximum timestamp in sync. + */ +#define XFS_DQ_BIGTIMEOUT_MAX (XFS_INO_BIGTIME_MAX) + /* * This is the main portion of the on-disk representation of quota * information for a user. This is the q_core of the struct xfs_dquot that diff --git a/fs/xfs/libxfs/xfs_quota_defs.h b/fs/xfs/libxfs/xfs_quota_defs.h index c453611ade3b..4ac486fd20a8 100644 --- a/fs/xfs/libxfs/xfs_quota_defs.h +++ b/fs/xfs/libxfs/xfs_quota_defs.h @@ -26,6 +26,7 @@ typedef uint16_t xfs_qwarncnt_t; #define XFS_DQ_GROUP 0x0004 /* a group quota */ #define XFS_DQ_DIRTY 0x0008 /* dquot is dirty */ #define XFS_DQ_FREEING 0x0010 /* dquot is being torn down */ +#define XFS_DQ_BIGTIME 0x0020 /* big timestamp format */ #define XFS_DQ_ALLTYPES (XFS_DQ_USER|XFS_DQ_PROJ|XFS_DQ_GROUP) @@ -34,7 +35,8 @@ typedef uint16_t xfs_qwarncnt_t; { XFS_DQ_PROJ, "PROJ" }, \ { XFS_DQ_GROUP, "GROUP" }, \ { XFS_DQ_DIRTY, "DIRTY" }, \ - { XFS_DQ_FREEING, "FREEING" } + { XFS_DQ_FREEING, "FREEING" }, \ + { XFS_DQ_BIGTIME, "BIGTIME" } /* * We have the possibility of all three quota types being active at once, and @@ -144,7 +146,10 @@ extern xfs_failaddr_t xfs_dqblk_verify(struct xfs_mount *mp, extern int xfs_calc_dquots_per_chunk(unsigned int nbblks); extern void xfs_dqblk_repair(struct xfs_mount *mp, struct xfs_dqblk *dqb, xfs_dqid_t id, uint type); -void xfs_dquot_from_disk_timestamp(struct timespec64 *tv, __be32 dtimer); -void xfs_dquot_to_disk_timestamp(__be32 *dtimer, const struct timespec64 *tv); +void xfs_dquot_from_disk_timestamp(struct xfs_disk_dquot *ddq, + struct timespec64 *tv, __be32 dtimer); +void xfs_dquot_to_disk_timestamp(struct xfs_disk_dquot *ddq, + __be32 *dtimer, const struct timespec64 *tv); +bool xfs_dquot_add_bigtime(struct xfs_mount *mp, struct xfs_disk_dquot *ddq); #endif /* __XFS_QUOTA_H__ */ diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 64e24fe5dcb2..6ff402bc0a3e 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -135,6 +135,11 @@ xchk_quota_item( if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0)) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); + /* incore should always have bigtime iflag set except for root */ + if (xfs_sb_version_hasbigtime(&mp->m_sb) && d->d_id && + !(d->d_flags & XFS_DQ_BIGTIME)) + xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); + /* Check the limits. */ bhard = be64_to_cpu(d->d_blk_hardlimit); ihard = be64_to_cpu(d->d_ino_hardlimit); diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 763e974f7aad..88b37098c6f4 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -118,35 +118,42 @@ xfs_quota_exceeded( */ static inline time64_t xfs_dquot_clamp_timer( - time64_t timer) + struct xfs_disk_dquot *ddq, + time64_t timer) { + if (ddq->d_flags & XFS_DQ_BIGTIME) + return clamp_t(time64_t, timer, XFS_DQ_BIGTIMEOUT_MIN, + XFS_DQ_BIGTIMEOUT_MAX); return clamp_t(time64_t, timer, XFS_DQ_TIMEOUT_MIN, XFS_DQ_TIMEOUT_MAX); } /* Set a quota grace period expiration timer. */ static inline void xfs_quota_set_timer( + struct xfs_disk_dquot *ddq, time64_t *itimer, __be32 *dtimer, - time_t limit) + time64_t limit) { struct timespec64 tv = { 0 }; - tv.tv_sec = xfs_dquot_clamp_timer(ktime_get_real_seconds() + limit); + tv.tv_sec = xfs_dquot_clamp_timer(ddq, + ktime_get_real_seconds() + limit); *itimer = tv.tv_sec; - xfs_dquot_to_disk_timestamp(dtimer, &tv); + xfs_dquot_to_disk_timestamp(ddq, dtimer, &tv); } /* Clear a quota grace period expiration timer. */ static inline void xfs_quota_clear_timer( + struct xfs_disk_dquot *ddq, time64_t *itimer, __be32 *dtimer) { struct timespec64 tv = { 0 }; *itimer = tv.tv_sec; - xfs_dquot_to_disk_timestamp(dtimer, &tv); + xfs_dquot_to_disk_timestamp(ddq, dtimer, &tv); } /* @@ -188,14 +195,14 @@ xfs_qm_adjust_dqtimers( &d->d_blk_softlimit, &d->d_blk_hardlimit); if (!dqp->q_btimer) { if (over) { - xfs_quota_set_timer(&dqp->q_btimer, &d->d_btimer, + xfs_quota_set_timer(d, &dqp->q_btimer, &d->d_btimer, mp->m_quotainfo->qi_btimelimit); } else { d->d_bwarns = 0; } } else { if (!over) { - xfs_quota_clear_timer(&dqp->q_btimer, &d->d_btimer); + xfs_quota_clear_timer(d, &dqp->q_btimer, &d->d_btimer); } } @@ -203,14 +210,14 @@ xfs_qm_adjust_dqtimers( &d->d_ino_softlimit, &d->d_ino_hardlimit); if (!dqp->q_itimer) { if (over) { - xfs_quota_set_timer(&dqp->q_itimer, &d->d_itimer, + xfs_quota_set_timer(d, &dqp->q_itimer, &d->d_itimer, mp->m_quotainfo->qi_itimelimit); } else { d->d_iwarns = 0; } } else { if (!over) { - xfs_quota_clear_timer(&dqp->q_itimer, &d->d_itimer); + xfs_quota_clear_timer(d, &dqp->q_itimer, &d->d_itimer); } } @@ -218,14 +225,16 @@ xfs_qm_adjust_dqtimers( &d->d_rtb_softlimit, &d->d_rtb_hardlimit); if (!dqp->q_rtbtimer) { if (over) { - xfs_quota_set_timer(&dqp->q_rtbtimer, &d->d_rtbtimer, + xfs_quota_set_timer(d, &dqp->q_rtbtimer, + &d->d_rtbtimer, mp->m_quotainfo->qi_rtbtimelimit); } else { d->d_rtbwarns = 0; } } else { if (!over) { - xfs_quota_clear_timer(&dqp->q_rtbtimer, &d->d_rtbtimer); + xfs_quota_clear_timer(d, &dqp->q_rtbtimer, + &d->d_rtbtimer); } } } @@ -261,6 +270,7 @@ xfs_qm_init_dquot_blk( d->dd_diskdq.d_version = XFS_DQUOT_VERSION; d->dd_diskdq.d_id = cpu_to_be32(curid); d->dd_diskdq.d_flags = type; + xfs_dquot_add_bigtime(mp, &d->dd_diskdq); if (xfs_sb_version_hascrc(&mp->m_sb)) { uuid_copy(&d->dd_uuid, &mp->m_sb.sb_meta_uuid); xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk), @@ -528,9 +538,10 @@ xfs_dquot_from_disk( { struct timespec64 tv; struct xfs_disk_dquot *ddqp = bp->b_addr + dqp->q_bufoffset; + struct xfs_disk_dquot *iddq = &dqp->q_core; /* copy everything from disk dquot to the incore dquot */ - memcpy(&dqp->q_core, ddqp, sizeof(struct xfs_disk_dquot)); + memcpy(iddq, ddqp, sizeof(struct xfs_disk_dquot)); /* * Reservation counters are defined as reservation plus current usage @@ -540,13 +551,28 @@ xfs_dquot_from_disk( dqp->q_res_icount = be64_to_cpu(ddqp->d_icount); dqp->q_res_rtbcount = be64_to_cpu(ddqp->d_rtbcount); - xfs_dquot_from_disk_timestamp(&tv, ddqp->d_btimer); + xfs_dquot_from_disk_timestamp(ddqp, &tv, ddqp->d_btimer); dqp->q_btimer = tv.tv_sec; - xfs_dquot_from_disk_timestamp(&tv, ddqp->d_itimer); + xfs_dquot_from_disk_timestamp(ddqp, &tv, ddqp->d_itimer); dqp->q_itimer = tv.tv_sec; - xfs_dquot_from_disk_timestamp(&tv, ddqp->d_rtbtimer); + xfs_dquot_from_disk_timestamp(ddqp, &tv, ddqp->d_rtbtimer); dqp->q_rtbtimer = tv.tv_sec; + /* Upgrade to bigtime if possible. */ + if (xfs_dquot_add_bigtime(dqp->q_mount, iddq)) { + tv.tv_sec = xfs_dquot_clamp_timer(iddq, dqp->q_btimer); + xfs_dquot_to_disk_timestamp(iddq, &iddq->d_btimer, &tv); + dqp->q_btimer = tv.tv_sec; + + tv.tv_sec = xfs_dquot_clamp_timer(iddq, dqp->q_itimer); + xfs_dquot_to_disk_timestamp(iddq, &iddq->d_itimer, &tv); + dqp->q_itimer = tv.tv_sec; + + tv.tv_sec = xfs_dquot_clamp_timer(iddq, dqp->q_rtbtimer); + xfs_dquot_to_disk_timestamp(iddq, &iddq->d_rtbtimer, &tv); + dqp->q_rtbtimer = tv.tv_sec; + } + /* initialize the dquot speculative prealloc thresholds */ xfs_dquot_set_prealloc_limits(dqp); } @@ -1175,6 +1201,21 @@ xfs_qm_dqflush( /* This is the only portion of data that needs to persist */ memcpy(ddqp, &dqp->q_core, sizeof(struct xfs_disk_dquot)); + /* + * We should never write non-bigtime dquots to a bigtime fs, except for + * the root dquot. + */ + if (!(dqp->q_core.d_flags & XFS_DQ_BIGTIME) && dqp->q_core.d_id && + xfs_sb_version_hasbigtime(&mp->m_sb)) { + xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS", + be32_to_cpu(ddqp->d_id), __this_address); + xfs_buf_relse(bp); + xfs_dqfunlock(dqp); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + xfs_quota_mark_sick(mp, dqp->dq_flags); + return -EFSCORRUPTED; + } + /* * Clear the dirty field and remember the flush lsn for later use. */ diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h index 86b9e0e07f84..940522f78a37 100644 --- a/fs/xfs/xfs_ondisk.h +++ b/fs/xfs/xfs_ondisk.h @@ -32,6 +32,12 @@ xfs_check_ondisk_structs(void) XFS_CHECK_VALUE(XFS_DQ_TIMEOUT_MAX, 4294967295LL); XFS_CHECK_VALUE(XFS_DQ_GRACE_MIN, 0LL); XFS_CHECK_VALUE(XFS_DQ_GRACE_MAX, 4294967295LL); + XFS_CHECK_VALUE(XFS_DQ_BIGTIMEOUT_MIN, 1LL); + XFS_CHECK_VALUE(XFS_DQ_BIGTIMEOUT_MAX, 16299260425LL); + BUILD_BUG_ON_MSG((XFS_DQ_TIMEOUT_MAX << XFS_DQ_BIGTIME_SHIFT) < + XFS_DQ_BIGTIMEOUT_MAX, + "XFS: quota timeout field is not large enough to fit " + "XFS_DQ_BIGTIMEOUT_MAX"); /* ag/file structures */ XFS_CHECK_STRUCT_SIZE(struct xfs_acl, 4); diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 9be123a0902e..fb671be5ca25 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -630,15 +630,15 @@ xfs_qm_init_timelimits( * more writing. If it is zero, a default is used. */ if (ddqp->d_btimer) { - xfs_dquot_from_disk_timestamp(&tv, ddqp->d_btimer); + xfs_dquot_from_disk_timestamp(ddqp, &tv, ddqp->d_btimer); qinf->qi_btimelimit = tv.tv_sec; } if (ddqp->d_itimer) { - xfs_dquot_from_disk_timestamp(&tv, ddqp->d_itimer); + xfs_dquot_from_disk_timestamp(ddqp, &tv, ddqp->d_itimer); qinf->qi_itimelimit = tv.tv_sec; } if (ddqp->d_rtbtimer) { - xfs_dquot_from_disk_timestamp(&tv, ddqp->d_rtbtimer); + xfs_dquot_from_disk_timestamp(ddqp, &tv, ddqp->d_rtbtimer); qinf->qi_rtbtimelimit = tv.tv_sec; } if (ddqp->d_bwarns) @@ -860,17 +860,17 @@ xfs_qm_reset_dqintervals( if (qinf->qi_btimelimit != XFS_QM_BTIMELIMIT) { tv.tv_sec = qinf->qi_btimelimit; - xfs_dquot_to_disk_timestamp(&ddq->d_btimer, &tv); + xfs_dquot_to_disk_timestamp(ddq, &ddq->d_btimer, &tv); } if (qinf->qi_itimelimit != XFS_QM_ITIMELIMIT) { tv.tv_sec = qinf->qi_itimelimit; - xfs_dquot_to_disk_timestamp(&ddq->d_itimer, &tv); + xfs_dquot_to_disk_timestamp(ddq, &ddq->d_itimer, &tv); } if (qinf->qi_rtbtimelimit != XFS_QM_RTBTIMELIMIT) { tv.tv_sec = qinf->qi_rtbtimelimit; - xfs_dquot_to_disk_timestamp(&ddq->d_rtbtimer, &tv); + xfs_dquot_to_disk_timestamp(ddq, &ddq->d_rtbtimer, &tv); } } @@ -928,6 +928,7 @@ xfs_qm_reset_dqcounts( ddq->d_rtbwarns = 0; if (!ddq->d_id) xfs_qm_reset_dqintervals(mp, ddq); + xfs_dquot_add_bigtime(mp, ddq); if (xfs_sb_version_hascrc(&mp->m_sb)) { xfs_update_cksum((char *)&dqb[j], diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index a3d9932f2e65..d3b69524ca18 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -64,9 +64,9 @@ struct xfs_quotainfo { struct xfs_inode *qi_pquotaip; /* project quota inode */ struct list_lru qi_lru; int qi_dquots; - time_t qi_btimelimit; /* limit for blks timer */ - time_t qi_itimelimit; /* limit for inodes timer */ - time_t qi_rtbtimelimit;/* limit for rt blks timer */ + time64_t qi_btimelimit; /* limit for blks timer */ + time64_t qi_itimelimit; /* limit for inodes timer */ + time64_t qi_rtbtimelimit;/* limit for rt blks timer */ xfs_qwarncnt_t qi_bwarnlimit; /* limit for blks warnings */ xfs_qwarncnt_t qi_iwarnlimit; /* limit for inodes warnings */ xfs_qwarncnt_t qi_rtbwarnlimit;/* limit for rt blks warnings */ @@ -84,7 +84,7 @@ xfs_dquot_tree( struct xfs_quotainfo *qi, int type) { - switch (type) { + switch (type & XFS_DQ_ALLTYPES) { case XFS_DQ_USER: return &qi->qi_uquota_tree; case XFS_DQ_GROUP: diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index bd9db42b89b9..47b63f820f50 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -441,7 +441,8 @@ xfs_qm_scall_quotaon( /* Set a new quota grace period. */ static inline void xfs_qm_set_grace( - time_t *qi_limit, + struct xfs_disk_dquot *ddq, + time64_t *qi_limit, time64_t *itimer, __be32 *dtimer, const s64 grace) @@ -452,7 +453,7 @@ xfs_qm_set_grace( XFS_DQ_GRACE_MAX); *qi_limit = tv.tv_sec; *itimer = tv.tv_sec; - xfs_dquot_to_disk_timestamp(dtimer, &tv); + xfs_dquot_to_disk_timestamp(ddq, dtimer, &tv); } #define XFS_QC_MASK \ @@ -585,14 +586,14 @@ xfs_qm_scall_setqlim( * for warnings. */ if (newlim->d_fieldmask & QC_SPC_TIMER) - xfs_qm_set_grace(&q->qi_btimelimit, &dqp->q_btimer, + xfs_qm_set_grace(ddq, &q->qi_btimelimit, &dqp->q_btimer, &ddq->d_btimer, newlim->d_spc_timer); if (newlim->d_fieldmask & QC_INO_TIMER) - xfs_qm_set_grace(&q->qi_itimelimit, &dqp->q_itimer, + xfs_qm_set_grace(ddq, &q->qi_itimelimit, &dqp->q_itimer, &ddq->d_itimer, newlim->d_ino_timer); if (newlim->d_fieldmask & QC_RT_SPC_TIMER) - xfs_qm_set_grace(&q->qi_rtbtimelimit, &dqp->q_rtbtimer, - &ddq->d_rtbtimer, + xfs_qm_set_grace(ddq, &q->qi_rtbtimelimit, + &dqp->q_rtbtimer, &ddq->d_rtbtimer, newlim->d_rt_spc_timer); if (newlim->d_fieldmask & QC_SPC_WARNS) q->qi_bwarnlimit = newlim->d_spc_warns; @@ -658,11 +659,11 @@ xfs_qm_scall_getquota_fill_qc( * so return zeroes in that case. */ if ((!XFS_IS_UQUOTA_ENFORCED(mp) && - dqp->q_core.d_flags == XFS_DQ_USER) || + (dqp->q_core.d_flags & XFS_DQ_ALLTYPES) == XFS_DQ_USER) || (!XFS_IS_GQUOTA_ENFORCED(mp) && - dqp->q_core.d_flags == XFS_DQ_GROUP) || + (dqp->q_core.d_flags & XFS_DQ_ALLTYPES) == XFS_DQ_GROUP) || (!XFS_IS_PQUOTA_ENFORCED(mp) && - dqp->q_core.d_flags == XFS_DQ_PROJ)) { + (dqp->q_core.d_flags & XFS_DQ_ALLTYPES) == XFS_DQ_PROJ)) { dst->d_spc_timer = 0; dst->d_ino_timer = 0; dst->d_rt_spc_timer = 0;