Re: [PATCH v8 2/5] xfs: Add pquota fields where gquota is used.

Jeff Liu
2013-05-13T03:59:53Z

Hi,

On 05/11/2013 05:21 AM, Chandra Seetharaman wrote:
> Add project quota changes to all the places where group quota field
> is used:
> * add separate project quota members into various structures
> * split project quota and group quotas so that instead of overriding
> the group quota members incore, the new project quota members are
> used instead
> * get rid of usage of the OQUOTA flag incore, in favor of separate
> * group
> and project quota flags.
> * add a project dquot argument to various functions.
>
> No externally visible interfaces changed.
>
> Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
> ---
> fs/xfs/xfs_dquot.c | 33 ++++-
> fs/xfs/xfs_dquot.h | 13 +-
> fs/xfs/xfs_icache.c | 4 +-
> fs/xfs/xfs_inode.h | 1 +
> fs/xfs/xfs_ioctl.c | 14 +-
> fs/xfs/xfs_iops.c | 4 +-
> fs/xfs/xfs_qm.c | 355 ++++++++++++++++++++++++++++++----------------
> fs/xfs/xfs_qm.h | 53 +++++---
> fs/xfs/xfs_qm_bhv.c | 2 +-
> fs/xfs/xfs_qm_syscalls.c | 19 ++-
> fs/xfs/xfs_quota.h | 32 +++--
> fs/xfs/xfs_super.c | 5 +-
> fs/xfs/xfs_symlink.c | 13 ++-
> fs/xfs/xfs_trans_dquot.c | 94 +++++++------
> fs/xfs/xfs_vnodeops.c | 12 +-
> 15 files changed, 416 insertions(+), 238 deletions(-)
>
> diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
> index a41f8bf..a25ba5d 100644
> --- a/fs/xfs/xfs_dquot.c
> +++ b/fs/xfs/xfs_dquot.c
> @@ -68,8 +68,7 @@ static struct lock_class_key xfs_dquot_other_class;
> * This is called to free all the memory associated with a dquot
> */
> void
> -xfs_qm_dqdestroy(
> - xfs_dquot_t *dqp)
> +xfs_qm_dqdestroy(xfs_dquot_t *dqp)
> {
> ASSERT(list_empty(&dqp->q_lru));
>
> @@ -568,6 +567,17 @@ xfs_qm_dqrepair(
> return 0;
> }
>
> +struct xfs_inode *
> +xfs_dq_to_quota_inode(struct xfs_dquot *dqp)
> +{
> + if (XFS_QM_ISUDQ(dqp))
> + return dqp->q_mount->m_quotainfo->qi_uquotaip;
> + if (XFS_QM_ISGDQ(dqp))
> + return dqp->q_mount->m_quotainfo->qi_gquotaip;
> + ASSERT(XFS_QM_ISPDQ(dqp));
> + return dqp->q_mount->m_quotainfo->qi_pquotaip;
> +}
Is it better to replace above conditional judgment with 'switch...case'?
i.e.
static inline struct xfs_inode *
xfs_dq_to_qip(struct xfs_dquot *dqp)
{
switch (dqp->dq_flags) {
case XFS_DQ_USER:
return dqp->q_mount->m_quotainfo->qi_uquotaip;
case XFS_DQ_GROUP:
return dqp->q_mount->m_quotainfo->qi_gqoutaip;
case XFS_DQ_PROJ:
return dqp->q_mount->m_quotainfo->qi_pquotaip;
}

ASSERT(0);
return NULL;
}
> +
> /*
> * Maps a dquot to the buffer containing its on-disk version.
> * This returns a ptr to the buffer containing the on-disk dquot
> @@ -584,7 +594,7 @@ xfs_qm_dqtobp(
> xfs_bmbt_irec_t map;
> int nmaps = 1, error;
> xfs_buf_t *bp;
> - xfs_inode_t *quotip = XFS_DQ_TO_QIP(dqp);
> + xfs_inode_t *quotip = xfs_dq_to_quota_inode(dqp);
> xfs_mount_t *mp = dqp->q_mount;
> xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id);
> xfs_trans_t *tp = (tpp ? *tpp : NULL);
> @@ -815,7 +825,7 @@ xfs_qm_dqget(
> xfs_dquot_t **O_dqpp) /* OUT : locked incore dquot */
> {
> struct xfs_quotainfo *qi = mp->m_quotainfo;
> - struct radix_tree_root *tree = XFS_DQUOT_TREE(qi, type);
> + struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
> struct xfs_dquot *dqp;
> int error;
>
> @@ -947,6 +957,7 @@ xfs_qm_dqput_final(
> {
> struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
> struct xfs_dquot *gdqp;
> + struct xfs_dquot *pdqp;
>
> trace_xfs_dqput_free(dqp);
>
> @@ -960,21 +971,29 @@ xfs_qm_dqput_final(
>
> /*
> * If we just added a udquot to the freelist, then we want to release
> - * the gdquot reference that it (probably) has. Otherwise it'll keep
> - * the gdquot from getting reclaimed.
> + * the gdquot/pdquot reference that it (probably) has. Otherwise it'll
> + * keep the gdquot/pdquot from getting reclaimed.
> */
> gdqp = dqp->q_gdquot;
> if (gdqp) {
> xfs_dqlock(gdqp);
> dqp->q_gdquot = NULL;
> }
> +
> + pdqp = dqp->q_pdquot;
> + if (pdqp) {
> + xfs_dqlock(pdqp);
> + dqp->q_pdquot = NULL;
> + }
> xfs_dqunlock(dqp);
>
> /*
> - * If we had a group quota hint, release it now.
> + * If we had a group/project quota hint, release it now.
> */
> if (gdqp)
> xfs_qm_dqput(gdqp);
> + if (pdqp)
> + xfs_qm_dqput(pdqp);
> }
>
> /*
> diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h
> index 4f0ebfc..00ccbf1 100644
> --- a/fs/xfs/xfs_dquot.h
> +++ b/fs/xfs/xfs_dquot.h
> @@ -29,7 +29,6 @@
> * when quotas are off.
> */
>
> -struct xfs_mount;
> struct xfs_trans;
>
> enum {
> @@ -52,7 +51,8 @@ typedef struct xfs_dquot {
> int q_bufoffset; /* off of dq in buffer (# dquots) */
> xfs_fileoff_t q_fileoffset; /* offset in quotas file */
>
> - struct xfs_dquot*q_gdquot; /* group dquot, hint only */
> + struct xfs_dquot *q_gdquot; /* group dquot, hint only */
> + struct xfs_dquot *q_pdquot; /* project dquot, hint only */
> xfs_disk_dquot_t q_core; /* actual usage & quotas */
> xfs_dq_logitem_t q_logitem; /* dquot log item */
> xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
> @@ -118,8 +118,9 @@ static inline int xfs_this_quota_on(struct xfs_mount *mp, int type)
> case XFS_DQ_USER:
> return XFS_IS_UQUOTA_ON(mp);
> case XFS_DQ_GROUP:
> + return XFS_IS_GQUOTA_ON(mp);
> case XFS_DQ_PROJ:
> - return XFS_IS_OQUOTA_ON(mp);
> + return XFS_IS_PQUOTA_ON(mp);
> default:
> return 0;
> }
> @@ -131,8 +132,9 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type)
> case XFS_DQ_USER:
> return ip->i_udquot;
> case XFS_DQ_GROUP:
> - case XFS_DQ_PROJ:
> return ip->i_gdquot;
> + case XFS_DQ_PROJ:
> + return ip->i_pdquot;
> default:
> return NULL;
> }
> @@ -144,9 +146,6 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type)
> #define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ)
> #define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQ_GROUP)
> #define XFS_DQ_TO_QINF(dqp) ((dqp)->q_mount->m_quotainfo)
For now, XFS_DQ_TO_QINF() is used by XFS_DQ_TO_QIP() only.
Maybe we can remove it in this patch as well, if we don't want to make
use of it in any other places to simplify the input...
> -#define XFS_DQ_TO_QIP(dqp) (XFS_QM_ISUDQ(dqp) ? \
> - XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \
> - XFS_DQ_TO_QINF(dqp)->qi_gquotaip)
>
> extern int xfs_qm_dqread(struct xfs_mount *, xfs_dqid_t, uint,
> uint, struct xfs_dquot **);
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index 96e344e..3f90e1c 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -335,7 +335,9 @@ xfs_iget_cache_miss(
> iflags = XFS_INEW;
> if (flags & XFS_IGET_DONTCACHE)
> iflags |= XFS_IDONTCACHE;
> - ip->i_udquot = ip->i_gdquot = NULL;
> + ip->i_udquot = NULL;
> + ip->i_gdquot = NULL;
> + ip->i_pdquot = NULL;
> xfs_iflags_set(ip, iflags);
>
> /* insert the new inode */
> diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
> index 9112979..b55fd34 100644
> --- a/fs/xfs/xfs_inode.h
> +++ b/fs/xfs/xfs_inode.h
> @@ -250,6 +250,7 @@ typedef struct xfs_inode {
> struct xfs_mount *i_mount; /* fs mount struct ptr */
> struct xfs_dquot *i_udquot; /* user dquot */
> struct xfs_dquot *i_gdquot; /* group dquot */
> + struct xfs_dquot *i_pdquot; /* project dquot */
>
> /* Inode location stuff */
> xfs_ino_t i_ino; /* inode number (agno/agino)*/
> diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
> index 5e99968..71a8bc5 100644
> --- a/fs/xfs/xfs_ioctl.c
> +++ b/fs/xfs/xfs_ioctl.c
> @@ -928,7 +928,7 @@ xfs_ioctl_setattr(
> struct xfs_trans *tp;
> unsigned int lock_flags = 0;
> struct xfs_dquot *udqp = NULL;
> - struct xfs_dquot *gdqp = NULL;
> + struct xfs_dquot *pdqp = NULL;
> struct xfs_dquot *olddquot = NULL;
> int code;
>
> @@ -957,7 +957,7 @@ xfs_ioctl_setattr(
> if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
> code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
> ip->i_d.di_gid, fa->fsx_projid,
> - XFS_QMOPT_PQUOTA, &udqp, &gdqp);
> + XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
> if (code)
> return code;
> }
> @@ -994,8 +994,8 @@ xfs_ioctl_setattr(
> XFS_IS_PQUOTA_ON(mp) &&
> xfs_get_projid(ip) != fa->fsx_projid) {
> ASSERT(tp);
> - code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
> - capable(CAP_FOWNER) ?
> + code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
> + pdqp, capable(CAP_FOWNER) ?
> XFS_QMOPT_FORCE_RES : 0);
> if (code) /* out of quota */
> goto error_return;
> @@ -1113,7 +1113,7 @@ xfs_ioctl_setattr(
> if (xfs_get_projid(ip) != fa->fsx_projid) {
> if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
> olddquot = xfs_qm_vop_chown(tp, ip,
> - &ip->i_gdquot, gdqp);
> + &ip->i_pdquot, pdqp);
> }
> xfs_set_projid(ip, fa->fsx_projid);
>
> @@ -1160,13 +1160,13 @@ xfs_ioctl_setattr(
> */
> xfs_qm_dqrele(olddquot);
> xfs_qm_dqrele(udqp);
> - xfs_qm_dqrele(gdqp);
> + xfs_qm_dqrele(pdqp);
>
> return code;
>
> error_return:
> xfs_qm_dqrele(udqp);
> - xfs_qm_dqrele(gdqp);
> + xfs_qm_dqrele(pdqp);
> xfs_trans_cancel(tp, 0);
> if (lock_flags)
> xfs_iunlock(ip, lock_flags);
> diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
> index d82efaa..7c54ea4 100644
> --- a/fs/xfs/xfs_iops.c
> +++ b/fs/xfs/xfs_iops.c
> @@ -517,7 +517,7 @@ xfs_setattr_nonsize(
> ASSERT(udqp == NULL);
> ASSERT(gdqp == NULL);
> error = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip),
> - qflags, &udqp, &gdqp);
> + qflags, &udqp, &gdqp, NULL);
> if (error)
> return error;
> }
> @@ -553,7 +553,7 @@ xfs_setattr_nonsize(
> (XFS_IS_GQUOTA_ON(mp) && igid != gid))) {
> ASSERT(tp);
> error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
> - capable(CAP_FOWNER) ?
> + NULL, capable(CAP_FOWNER) ?
> XFS_QMOPT_FORCE_RES : 0);
> if (error) /* out of quota */
> goto out_trans_cancel;
> diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
> index fe4c743..97912cb 100644
> --- a/fs/xfs/xfs_qm.c
> +++ b/fs/xfs/xfs_qm.c
> @@ -69,7 +69,7 @@ xfs_qm_dquot_walk(
> void *data)
> {
> struct xfs_quotainfo *qi = mp->m_quotainfo;
> - struct radix_tree_root *tree = XFS_DQUOT_TREE(qi, type);
> + struct radix_tree_root *tree = xfs_dquot_tree(qi, type);
> uint32_t next_index;
> int last_error = 0;
> int skipped;
> @@ -136,6 +136,7 @@ xfs_qm_dqpurge(
> struct xfs_mount *mp = dqp->q_mount;
> struct xfs_quotainfo *qi = mp->m_quotainfo;
> struct xfs_dquot *gdqp = NULL;
> + struct xfs_dquot *pdqp = NULL;
>
> xfs_dqlock(dqp);
> if ((dqp->dq_flags & XFS_DQ_FREEING) || dqp->q_nrefs != 0) {
> @@ -144,8 +145,7 @@ xfs_qm_dqpurge(
> }
>
> /*
> - * If this quota has a group hint attached, prepare for releasing it
> - * now.
> + * If this quota has a hint attached, prepare for releasing it now.
> */
> gdqp = dqp->q_gdquot;
> if (gdqp) {
> @@ -153,6 +153,12 @@ xfs_qm_dqpurge(
> dqp->q_gdquot = NULL;
> }
>
> + pdqp = dqp->q_pdquot;
> + if (pdqp) {
> + xfs_dqlock(pdqp);
> + dqp->q_pdquot = NULL;
> + }
> +
> dqp->dq_flags |= XFS_DQ_FREEING;
>
> xfs_dqflock(dqp);
> @@ -188,7 +194,7 @@ xfs_qm_dqpurge(
> xfs_dqfunlock(dqp);
> xfs_dqunlock(dqp);
>
> - radix_tree_delete(XFS_DQUOT_TREE(qi, dqp->q_core.d_flags),
> + radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags),
> be32_to_cpu(dqp->q_core.d_id));
> qi->qi_dquots--;
>
> @@ -207,6 +213,8 @@ xfs_qm_dqpurge(
>
> if (gdqp)
> xfs_qm_dqput(gdqp);
> + if (pdqp)
> + xfs_qm_dqput(pdqp);
> return 0;
> }
>
> @@ -363,6 +371,10 @@ xfs_qm_unmount_quotas(
> IRELE(mp->m_quotainfo->qi_gquotaip);
> mp->m_quotainfo->qi_gquotaip = NULL;
> }
> + if (mp->m_quotainfo->qi_pquotaip) {
> + IRELE(mp->m_quotainfo->qi_pquotaip);
> + mp->m_quotainfo->qi_pquotaip = NULL;
> + }
> }
> }
>
> @@ -409,7 +421,10 @@ xfs_qm_dqattach_one(
> * be reclaimed as long as we have a ref from inode and we
> * hold the ilock.
> */
> - dqp = udqhint->q_gdquot;
> + if (type == XFS_DQ_GROUP)
> + dqp = udqhint->q_gdquot;
> + else
> + dqp = udqhint->q_pdquot;
Maybe it's better to:
dqp = "type == XFS_DQ_GROUP" ? udqhint->q_gdquot :
udqhint->q_pdquot;

> if (dqp && be32_to_cpu(dqp->q_core.d_id) == id) {
> ASSERT(*IO_idqpp == NULL);
>
> @@ -452,28 +467,38 @@ xfs_qm_dqattach_one(
>
>
> /*
> - * Given a udquot and gdquot, attach a ptr to the group dquot in the
> + * Given a udquot and gdquot, attach a ptr to the group/project dquot in the
> * udquot as a hint for future lookups.
> */
> STATIC void
> -xfs_qm_dqattach_grouphint(
> - xfs_dquot_t *udq,
> - xfs_dquot_t *gdq)
> +xfs_qm_dqattach_grouphint(xfs_inode_t *ip, int type)
> {
> - xfs_dquot_t *tmp;
> + struct xfs_dquot **dqhint;
> + struct xfs_dquot *gpdq;
> + struct xfs_dquot *udq = ip->i_udquot;
>
> xfs_dqlock(udq);
>
> - tmp = udq->q_gdquot;
> - if (tmp) {
> - if (tmp == gdq)
> + if (type == XFS_DQ_GROUP) {
> + gpdq = ip->i_gdquot;
> + dqhint = &udq->q_gdquot;
> + } else {
> + gpdq = ip->i_pdquot;
> + dqhint = &udq->q_pdquot;
> + }
> +
> + if (*dqhint) {
> + struct xfs_dquot *tmp;
> +
> + if (*dqhint == gpdq)
> goto done;
>
> - udq->q_gdquot = NULL;
> + tmp = *dqhint;
> + *dqhint = NULL;
> xfs_qm_dqrele(tmp);
> }
>
> - udq->q_gdquot = xfs_qm_dqhold(gdq);
> + *dqhint = xfs_qm_dqhold(gpdq);
> done:
> xfs_dqunlock(udq);
> }
> @@ -527,12 +552,8 @@ xfs_qm_dqattach_locked(
> }
>
> ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
> - if (XFS_IS_OQUOTA_ON(mp)) {
> - error = XFS_IS_GQUOTA_ON(mp) ?
> - xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
> - flags & XFS_QMOPT_DQALLOC,
> - ip->i_udquot, &ip->i_gdquot) :
> - xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
> + if (XFS_IS_GQUOTA_ON(mp)) {
> + error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
> flags & XFS_QMOPT_DQALLOC,
> ip->i_udquot, &ip->i_gdquot);
> /*
> @@ -544,14 +565,28 @@ xfs_qm_dqattach_locked(
> nquotas++;
> }
>
> + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
> + if (XFS_IS_PQUOTA_ON(mp)) {
> + error = xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
> + flags & XFS_QMOPT_DQALLOC,
> + ip->i_udquot, &ip->i_pdquot);
> + /*
> + * Don't worry about the udquot that we may have
> + * attached above. It'll get detached, if not already.
> + */
> + if (error)
> + goto done;
> + nquotas++;
> + }
> +
> /*
> - * Attach this group quota to the user quota as a hint.
> + * Attach this group/project quota to the user quota as a hint.
> * This WON'T, in general, result in a thrash.
> */
> - if (nquotas == 2) {
> + if (nquotas > 1 && ip->i_udquot) {
> ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
> - ASSERT(ip->i_udquot);
> - ASSERT(ip->i_gdquot);
> + ASSERT(ip->i_gdquot || !XFS_IS_GQUOTA_ON(mp));
> + ASSERT(ip->i_pdquot || !XFS_IS_PQUOTA_ON(mp));
>
> /*
> * We do not have i_udquot locked at this point, but this check
> @@ -559,8 +594,13 @@ xfs_qm_dqattach_locked(
> * 100% all the time. It is just a hint, and this will
> * succeed in general.
> */
> - if (ip->i_udquot->q_gdquot != ip->i_gdquot)
> - xfs_qm_dqattach_grouphint(ip->i_udquot, ip->i_gdquot);
> + if (XFS_IS_GQUOTA_ON(mp) &&
> + ip->i_udquot->q_gdquot != ip->i_gdquot)
> + xfs_qm_dqattach_grouphint(ip, XFS_DQ_GROUP);
> +
> + if (XFS_IS_PQUOTA_ON(mp) &&
> + ip->i_udquot->q_pdquot != ip->i_pdquot)
> + xfs_qm_dqattach_grouphint(ip, XFS_DQ_PROJ);
> }
>
> done:
> @@ -568,8 +608,10 @@ xfs_qm_dqattach_locked(
> if (!error) {
> if (XFS_IS_UQUOTA_ON(mp))
> ASSERT(ip->i_udquot);
> - if (XFS_IS_OQUOTA_ON(mp))
> + if (XFS_IS_GQUOTA_ON(mp))
> ASSERT(ip->i_gdquot);
> + if (XFS_IS_PQUOTA_ON(mp))
> + ASSERT(ip->i_pdquot);
> }
> ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
> #endif
> @@ -602,7 +644,7 @@ void
> xfs_qm_dqdetach(
> xfs_inode_t *ip)
> {
> - if (!(ip->i_udquot || ip->i_gdquot))
> + if (!(ip->i_udquot || ip->i_gdquot || ip->i_pdquot))
> return;
>
> trace_xfs_dquot_dqdetach(ip);
> @@ -617,6 +659,10 @@ xfs_qm_dqdetach(
> xfs_qm_dqrele(ip->i_gdquot);
> ip->i_gdquot = NULL;
> }
> + if (ip->i_pdquot) {
> + xfs_qm_dqrele(ip->i_pdquot);
> + ip->i_pdquot = NULL;
> + }
> }
>
> int
> @@ -661,6 +707,7 @@ xfs_qm_init_quotainfo(
>
> INIT_RADIX_TREE(&qinf->qi_uquota_tree, GFP_NOFS);
> INIT_RADIX_TREE(&qinf->qi_gquota_tree, GFP_NOFS);
> + INIT_RADIX_TREE(&qinf->qi_pquota_tree, GFP_NOFS);
> mutex_init(&qinf->qi_tree_lock);
>
> INIT_LIST_HEAD(&qinf->qi_lru_list);
> @@ -762,6 +809,10 @@ xfs_qm_destroy_quotainfo(
> IRELE(qi->qi_gquotaip);
> qi->qi_gquotaip = NULL;
> }
> + if (qi->qi_pquotaip) {
> + IRELE(qi->qi_pquotaip);
> + qi->qi_pquotaip = NULL;
> + }
> mutex_destroy(&qi->qi_quotaofflock);
> kmem_free(qi);
> mp->m_quotainfo = NULL;
> @@ -1247,16 +1298,18 @@ xfs_qm_quotacheck(
> int done, count, error, error2;
> xfs_ino_t lastino;
> size_t structsz;
> - xfs_inode_t *uip, *gip;
> uint flags;
> LIST_HEAD (buffer_list);
> + struct xfs_inode *uip = mp->m_quotainfo->qi_uquotaip;
> + struct xfs_inode *gip = mp->m_quotainfo->qi_gquotaip;
> + struct xfs_inode *pip = mp->m_quotainfo->qi_pquotaip;
>
> count = INT_MAX;
> structsz = 1;
> lastino = 0;
> flags = 0;
>
> - ASSERT(mp->m_quotainfo->qi_uquotaip || mp->m_quotainfo->qi_gquotaip);
> + ASSERT(uip || gip || pip);
> ASSERT(XFS_IS_QUOTA_RUNNING(mp));
>
> xfs_notice(mp, "Quotacheck needed: Please wait.");
> @@ -1266,7 +1319,6 @@ xfs_qm_quotacheck(
> * their counters to zero. We need a clean slate.
> * We don't log our changes till later.
> */
> - uip = mp->m_quotainfo->qi_uquotaip;
> if (uip) {
> error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA,
> &buffer_list);
> @@ -1275,15 +1327,20 @@ xfs_qm_quotacheck(
> flags |= XFS_UQUOTA_CHKD;
> }
>
> - gip = mp->m_quotainfo->qi_gquotaip;
> if (gip) {
> - error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ?
> - XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA,
> + error = xfs_qm_dqiterate(mp, gip, XFS_QMOPT_GQUOTA,
> + &buffer_list);
> + if (error)
> + goto error_return;
> + flags |= XFS_GQUOTA_CHKD;
> + }
> +
> + if (pip) {
> + error = xfs_qm_dqiterate(mp, pip, XFS_QMOPT_PQUOTA,
> &buffer_list);
> if (error)
> goto error_return;
> - flags |= XFS_IS_GQUOTA_ON(mp) ?
> - XFS_GQUOTA_CHKD : XFS_PQUOTA_CHKD;
> + flags |= XFS_PQUOTA_CHKD;
> }
>
> do {
> @@ -1378,13 +1435,14 @@ STATIC int
> xfs_qm_init_quotainos(
> xfs_mount_t *mp)
> {
> - xfs_inode_t *uip, *gip;
> + struct xfs_inode *uip = NULL;
> + struct xfs_inode *gip = NULL;
> + struct xfs_inode *pip = NULL;
> int error;
> __int64_t sbflags;
> uint flags;
>
> ASSERT(mp->m_quotainfo);
> - uip = gip = NULL;
> sbflags = 0;
> flags = 0;
>
> @@ -1395,19 +1453,27 @@ xfs_qm_init_quotainos(
> if (XFS_IS_UQUOTA_ON(mp) &&
> mp->m_sb.sb_uquotino != NULLFSINO) {
> ASSERT(mp->m_sb.sb_uquotino > 0);
> - if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
> - 0, 0, &uip)))
> + error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
> + 0, 0, &uip);
> + if (error)
> return XFS_ERROR(error);
> }
> - if (XFS_IS_OQUOTA_ON(mp) &&
> + if (XFS_IS_GQUOTA_ON(mp) &&
> mp->m_sb.sb_gquotino != NULLFSINO) {
> ASSERT(mp->m_sb.sb_gquotino > 0);
> - if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
> - 0, 0, &gip))) {
> - if (uip)
> - IRELE(uip);
> - return XFS_ERROR(error);
> - }
> + error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
> + 0, 0, &gip);
> + if (error)
> + goto error_rele;
> + }
> + /* Use sb_gquotino for now */
> + if (XFS_IS_PQUOTA_ON(mp) &&
> + mp->m_sb.sb_gquotino != NULLFSINO) {
> + ASSERT(mp->m_sb.sb_gquotino > 0);
> + error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
> + 0, 0, &pip);
> + if (error)
> + goto error_rele;
> }
> } else {
> flags |= XFS_QMOPT_SBVERSION;
> @@ -1416,36 +1482,50 @@ xfs_qm_init_quotainos(
> }
>
> /*
> - * Create the two inodes, if they don't exist already. The changes
> + * Create the three inodes, if they don't exist already. The changes
> * made above will get added to a transaction and logged in one of
> * the qino_alloc calls below. If the device is readonly,
> * temporarily switch to read-write to do this.
> */
> if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) {
> - if ((error = xfs_qm_qino_alloc(mp, &uip,
> + error = xfs_qm_qino_alloc(mp, &uip,
> sbflags | XFS_SB_UQUOTINO,
> - flags | XFS_QMOPT_UQUOTA)))
> + flags | XFS_QMOPT_UQUOTA);
> + if (error)
> return XFS_ERROR(error);
>
> flags &= ~XFS_QMOPT_SBVERSION;
> }
> - if (XFS_IS_OQUOTA_ON(mp) && gip == NULL) {
> - flags |= (XFS_IS_GQUOTA_ON(mp) ?
> - XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA);
> + if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) {
> error = xfs_qm_qino_alloc(mp, &gip,
> - sbflags | XFS_SB_GQUOTINO, flags);
> - if (error) {
> - if (uip)
> - IRELE(uip);
> -
> - return XFS_ERROR(error);
> - }
> + sbflags | XFS_SB_GQUOTINO,
> + flags | XFS_QMOPT_GQUOTA);
> + if (error)
> + goto error_rele;
> + }
> + if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) {
> + error = xfs_qm_qino_alloc(mp, &pip,
> + sbflags | XFS_SB_GQUOTINO,
> + flags | XFS_QMOPT_PQUOTA);
> + if (error)
> + goto error_rele;
> }
>
> mp->m_quotainfo->qi_uquotaip = uip;
> mp->m_quotainfo->qi_gquotaip = gip;
> + mp->m_quotainfo->qi_pquotaip = pip;
>
> return 0;
> +
> +error_rele:
> + if (uip)
> + IRELE(uip);
> + if (gip)
> + IRELE(gip);
> + if (pip)
> + IRELE(pip);
> + return XFS_ERROR(error);
> +
> }
>
> STATIC void
> @@ -1456,7 +1536,7 @@ xfs_qm_dqfree_one(
> struct xfs_quotainfo *qi = mp->m_quotainfo;
>
> mutex_lock(&qi->qi_tree_lock);
> - radix_tree_delete(XFS_DQUOT_TREE(qi, dqp->q_core.d_flags),
> + radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags),
> be32_to_cpu(dqp->q_core.d_id));
>
> qi->qi_dquots--;
> @@ -1639,10 +1719,13 @@ xfs_qm_vop_dqalloc(
> prid_t prid,
> uint flags,
> struct xfs_dquot **O_udqpp,
> - struct xfs_dquot **O_gdqpp)
> + struct xfs_dquot **O_gdqpp,
> + struct xfs_dquot **O_pdqpp)
> {
> struct xfs_mount *mp = ip->i_mount;
> - struct xfs_dquot *uq, *gq;
> + struct xfs_dquot *uq = NULL;
> + struct xfs_dquot *gq = NULL;
> + struct xfs_dquot *pq = NULL;
> int error;
> uint lockflags;
>
> @@ -1667,7 +1750,6 @@ xfs_qm_vop_dqalloc(
> }
> }
>
> - uq = gq = NULL;
> if ((flags & XFS_QMOPT_UQUOTA) && XFS_IS_UQUOTA_ON(mp)) {
> if (ip->i_d.di_uid != uid) {
> /*
> @@ -1680,14 +1762,14 @@ xfs_qm_vop_dqalloc(
> * holding ilock.
> */
> xfs_iunlock(ip, lockflags);
> - if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid,
> + error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid,
> XFS_DQ_USER,
> XFS_QMOPT_DQALLOC |
> XFS_QMOPT_DOWARN,
> - &uq))) {
> - ASSERT(error != ENOENT);
> + &uq);
> + ASSERT(error != ENOENT);
> + if (error)
> return error;
> - }
> /*
> * Get the ilock in the right order.
> */
> @@ -1706,16 +1788,15 @@ xfs_qm_vop_dqalloc(
> if ((flags & XFS_QMOPT_GQUOTA) && XFS_IS_GQUOTA_ON(mp)) {
> if (ip->i_d.di_gid != gid) {
> xfs_iunlock(ip, lockflags);
> - if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid,
> + error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid,
> XFS_DQ_GROUP,
> XFS_QMOPT_DQALLOC |
> XFS_QMOPT_DOWARN,
> - &gq))) {
> - if (uq)
> - xfs_qm_dqrele(uq);
> - ASSERT(error != ENOENT);
> - return error;
> - }
> + &gq);
> + ASSERT(error != ENOENT);
> + if (error)
> + goto error_rele;
> +
> xfs_dqunlock(gq);
> lockflags = XFS_ILOCK_SHARED;
> xfs_ilock(ip, lockflags);
> @@ -1723,25 +1804,25 @@ xfs_qm_vop_dqalloc(
> ASSERT(ip->i_gdquot);
> gq = xfs_qm_dqhold(ip->i_gdquot);
> }
> - } else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
> + }
> + if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
> if (xfs_get_projid(ip) != prid) {
> xfs_iunlock(ip, lockflags);
> - if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid,
> + error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid,
> XFS_DQ_PROJ,
> XFS_QMOPT_DQALLOC |
> XFS_QMOPT_DOWARN,
> - &gq))) {
> - if (uq)
> - xfs_qm_dqrele(uq);
> - ASSERT(error != ENOENT);
> - return (error);
> - }
> - xfs_dqunlock(gq);
> + &pq);
> + ASSERT(error != ENOENT);
> + if (error)
> + goto error_rele;
> +
> + xfs_dqunlock(pq);
> lockflags = XFS_ILOCK_SHARED;
> xfs_ilock(ip, lockflags);
> } else {
> - ASSERT(ip->i_gdquot);
> - gq = xfs_qm_dqhold(ip->i_gdquot);
> + ASSERT(ip->i_pdquot);
> + pq = xfs_qm_dqhold(ip->i_pdquot);
> }
> }
> if (uq)
> @@ -1756,7 +1837,18 @@ xfs_qm_vop_dqalloc(
> *O_gdqpp = gq;
> else if (gq)
> xfs_qm_dqrele(gq);
> + if (O_pdqpp)
> + *O_pdqpp = pq;
> + else if (pq)
> + xfs_qm_dqrele(pq);
> return 0;
> +
> +error_rele:
> + if (uq)
> + xfs_qm_dqrele(uq);
> + if (gq)
> + xfs_qm_dqrele(gq);
> + return error;
> }
>
> /*
> @@ -1804,15 +1896,21 @@ xfs_qm_vop_chown(
> */
> int
> xfs_qm_vop_chown_reserve(
> - xfs_trans_t *tp,
> - xfs_inode_t *ip,
> - xfs_dquot_t *udqp,
> - xfs_dquot_t *gdqp,
> - uint flags)
> + xfs_trans_t *tp,
> + xfs_inode_t *ip,
> + struct xfs_dquot *udqp,
> + struct xfs_dquot *gdqp,
> + struct xfs_dquot *pdqp,
> + uint flags)
> {
> xfs_mount_t *mp = ip->i_mount;
> uint delblks, blkflags, prjflags = 0;
> - xfs_dquot_t *unresudq, *unresgdq, *delblksudq, *delblksgdq;
> + struct xfs_dquot *unresudq = NULL;
> + struct xfs_dquot *unresgdq = NULL;
> + struct xfs_dquot *unrespdq = NULL;
> + struct xfs_dquot *delblksudq = NULL;
> + struct xfs_dquot *delblksgdq = NULL;
> + struct xfs_dquot *delblkspdq = NULL;
> int error;
>
>
> @@ -1820,7 +1918,6 @@ xfs_qm_vop_chown_reserve(
> ASSERT(XFS_IS_QUOTA_RUNNING(mp));
>
> delblks = ip->i_delayed_blks;
> - delblksudq = delblksgdq = unresudq = unresgdq = NULL;
> blkflags = XFS_IS_REALTIME_INODE(ip) ?
> XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS;
>
> @@ -1837,25 +1934,29 @@ xfs_qm_vop_chown_reserve(
> unresudq = ip->i_udquot;
> }
> }
> - 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 ||
> - (XFS_IS_GQUOTA_ON(ip->i_mount) &&
> - ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) {
> - delblksgdq = gdqp;
> - if (delblks) {
> - ASSERT(ip->i_gdquot);
> - unresgdq = ip->i_gdquot;
> - }
> + if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp &&
> + ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id)) {
> + delblksgdq = gdqp;
> + if (delblks) {
> + ASSERT(ip->i_gdquot);
> + unresgdq = ip->i_gdquot;
> + }
> + }
> +
> + if (XFS_IS_PQUOTA_ON(ip->i_mount) && pdqp &&
> + xfs_get_projid(ip) != be32_to_cpu(pdqp->q_core.d_id)) {
> + prjflags = XFS_QMOPT_ENOSPC;
> + delblkspdq = pdqp;
> + if (delblks) {
> + ASSERT(ip->i_pdquot);
> + unrespdq = ip->i_pdquot;
> }
> }
>
> - if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
> - delblksudq, delblksgdq, ip->i_d.di_nblocks, 1,
> - flags | blkflags | prjflags)))
> + error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
> + delblksudq, delblksgdq, delblkspdq, ip->i_d.di_nblocks,
> + 1, flags | blkflags | prjflags);
> + if (error)
> return (error);
>
> /*
> @@ -1868,15 +1969,17 @@ xfs_qm_vop_chown_reserve(
> /*
> * Do the reservations first. Unreservation can't fail.
> */
> - ASSERT(delblksudq || delblksgdq);
> - ASSERT(unresudq || unresgdq);
> - if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
> - delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0,
> - flags | blkflags | prjflags)))
> + ASSERT(delblksudq || delblksgdq || delblkspdq);
> + ASSERT(unresudq || unresgdq || unrespdq);
> + error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
> + delblksudq, delblksgdq, delblkspdq,
> + (xfs_qcnt_t)delblks, 0,
> + flags | blkflags | prjflags);
> + if (error)
> return (error);
> xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
> - unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0,
> - blkflags);
> + unresudq, unresgdq, unrespdq,
> + -((xfs_qcnt_t)delblks), 0, blkflags);
> }
>
> return (0);
> @@ -1915,7 +2018,8 @@ xfs_qm_vop_create_dqattach(
> struct xfs_trans *tp,
> struct xfs_inode *ip,
> struct xfs_dquot *udqp,
> - struct xfs_dquot *gdqp)
> + struct xfs_dquot *gdqp,
> + struct xfs_dquot *pdqp)
> {
> struct xfs_mount *mp = tp->t_mountp;
>
> @@ -1935,13 +2039,18 @@ xfs_qm_vop_create_dqattach(
> }
> if (gdqp) {
> ASSERT(ip->i_gdquot == NULL);
> - ASSERT(XFS_IS_OQUOTA_ON(mp));
> - ASSERT((XFS_IS_GQUOTA_ON(mp) ?
> - ip->i_d.di_gid : xfs_get_projid(ip)) ==
> - be32_to_cpu(gdqp->q_core.d_id));
> -
> + ASSERT(XFS_IS_GQUOTA_ON(mp));
> + ASSERT(ip->i_d.di_gid == be32_to_cpu(gdqp->q_core.d_id));
> ip->i_gdquot = xfs_qm_dqhold(gdqp);
> xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1);
> }
> + if (pdqp) {
> + ASSERT(ip->i_pdquot == NULL);
> + ASSERT(XFS_IS_PQUOTA_ON(mp));
> + ASSERT(xfs_get_projid(ip) == be32_to_cpu(pdqp->q_core.d_id));
> +
> + ip->i_pdquot = xfs_qm_dqhold(pdqp);
> + xfs_trans_mod_dquot(tp, pdqp, XFS_TRANS_DQ_ICOUNT, 1);
> + }
> }
>
> diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h
> index 5d16a6e..f23b06b 100644
> --- a/fs/xfs/xfs_qm.h
> +++ b/fs/xfs/xfs_qm.h
> @@ -44,9 +44,11 @@ extern struct kmem_zone *xfs_qm_dqtrxzone;
> typedef struct xfs_quotainfo {
> struct radix_tree_root qi_uquota_tree;
> struct radix_tree_root qi_gquota_tree;
> + struct radix_tree_root qi_pquota_tree;
> struct mutex qi_tree_lock;
> - xfs_inode_t *qi_uquotaip; /* user quota inode */
> - xfs_inode_t *qi_gquotaip; /* group quota inode */
> + struct xfs_inode *qi_uquotaip; /* user quota inode */
> + struct xfs_inode *qi_gquotaip; /* group quota inode */
> + struct xfs_inode *qi_pquotaip; /* project quota inode */
> struct list_head qi_lru_list;
> struct mutex qi_lru_lock;
> int qi_lru_count;
> @@ -69,30 +71,45 @@ typedef struct xfs_quotainfo {
> struct shrinker qi_shrinker;
> } xfs_quotainfo_t;
>
> -#define XFS_DQUOT_TREE(qi, type) \
> - ((type & XFS_DQ_USER) ? \
> - &((qi)->qi_uquota_tree) : \
> - &((qi)->qi_gquota_tree))
> -
> +static inline struct radix_tree_root *
> +xfs_dquot_tree(
> + struct xfs_quotainfo *qi,
> + int type)
> +{
> + switch (type) {
> + case XFS_DQ_USER:
> + return &qi->qi_uquota_tree;
> + case XFS_DQ_GROUP:
> + return &qi->qi_gquota_tree;
> + case XFS_DQ_PROJ:
> + return &qi->qi_pquota_tree;
> + default:
> + ASSERT(0);
> + }
> + return NULL;
> +}
>
> extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp,
> unsigned int nbblks);
> -extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
> -extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
> - xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
> -extern void xfs_trans_dqjoin(xfs_trans_t *, xfs_dquot_t *);
> -extern void xfs_trans_log_dquot(xfs_trans_t *, xfs_dquot_t *);
> +extern void xfs_trans_mod_dquot(xfs_trans_t *, struct xfs_dquot *, uint, long);
> +extern void xfs_trans_dqjoin(xfs_trans_t *, struct xfs_dquot *);
> +extern void xfs_trans_log_dquot(xfs_trans_t *, struct xfs_dquot *);
>
> /*
> - * We keep the usr and grp dquots separately so that locking will be easier
> - * to do at commit time. All transactions that we know of at this point
> + * We keep the usr, grp, and prj dquots separately so that locking will be
> + * easier to do at commit time. All transactions that we know of at this point
> * affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value.
> */
> +enum {
> + XFS_QM_TRANS_USR = 0,
> + XFS_QM_TRANS_GRP,
> + XFS_QM_TRANS_PROJ,
> + XFS_QM_TRANS_DQTYPES
> +};
> #define XFS_QM_TRANS_MAXDQS 2
> -typedef struct xfs_dquot_acct {
> - xfs_dqtrx_t dqa_usrdquots[XFS_QM_TRANS_MAXDQS];
> - xfs_dqtrx_t dqa_grpdquots[XFS_QM_TRANS_MAXDQS];
> -} xfs_dquot_acct_t;
> +struct xfs_dquot_acct {
> + struct xfs_dqtrx dqs[XFS_QM_TRANS_DQTYPES][XFS_QM_TRANS_MAXDQS];
> +};
>
> /*
> * Users are allowed to have a usage exceeding their softlimit for
> diff --git a/fs/xfs/xfs_qm_bhv.c b/fs/xfs/xfs_qm_bhv.c
> index 2d02eac..72a4fdd 100644
> --- a/fs/xfs/xfs_qm_bhv.c
> +++ b/fs/xfs/xfs_qm_bhv.c
> @@ -115,7 +115,7 @@ xfs_qm_newmount(
> (pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
> (!pquotaondisk && XFS_IS_PQUOTA_ON(mp)) ||
> (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
> - (!gquotaondisk && XFS_IS_OQUOTA_ON(mp))) &&
> + (!gquotaondisk && XFS_IS_GQUOTA_ON(mp))) &&
> xfs_dev_is_read_only(mp, "changing quota state")) {
> xfs_warn(mp, "please mount with%s%s%s%s.",
> (!quotaondisk ? "out quota" : ""),
> diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c
> index f005f1d..9bec772 100644
> --- a/fs/xfs/xfs_qm_syscalls.c
> +++ b/fs/xfs/xfs_qm_syscalls.c
> @@ -119,7 +119,8 @@ xfs_qm_scall_quotaoff(
> dqtype |= XFS_QMOPT_GQUOTA;
> flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD);
> inactivate_flags |= XFS_GQUOTA_ACTIVE;
> - } else if (flags & XFS_PQUOTA_ACCT) {
> + }
> + if (flags & XFS_PQUOTA_ACCT) {
> dqtype |= XFS_QMOPT_PQUOTA;
> flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD);
> inactivate_flags |= XFS_PQUOTA_ACTIVE;
> @@ -214,10 +215,14 @@ xfs_qm_scall_quotaoff(
> IRELE(q->qi_uquotaip);
> q->qi_uquotaip = NULL;
> }
> - if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) {
> + if ((dqtype & XFS_QMOPT_GQUOTA) && q->qi_gquotaip) {
> IRELE(q->qi_gquotaip);
> q->qi_gquotaip = NULL;
> }
> + if ((dqtype & XFS_QMOPT_PQUOTA) && q->qi_pquotaip) {
> + IRELE(q->qi_pquotaip);
> + q->qi_pquotaip = NULL;
> + }
>
> out_unlock:
> mutex_unlock(&q->qi_quotaofflock);
> @@ -853,9 +858,11 @@ xfs_dqrele_inode(
> {
> /* skip quota inodes */
> if (ip == ip->i_mount->m_quotainfo->qi_uquotaip ||
> - ip == ip->i_mount->m_quotainfo->qi_gquotaip) {
> + ip == ip->i_mount->m_quotainfo->qi_gquotaip ||
> + ip == ip->i_mount->m_quotainfo->qi_pquotaip) {
> ASSERT(ip->i_udquot == NULL);
> ASSERT(ip->i_gdquot == NULL);
> + ASSERT(ip->i_pdquot == NULL);
> return 0;
> }
>
> @@ -864,10 +871,14 @@ xfs_dqrele_inode(
> xfs_qm_dqrele(ip->i_udquot);
> ip->i_udquot = NULL;
> }
> - if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) {
> + if ((flags & XFS_GQUOTA_ACCT) && ip->i_gdquot) {
> xfs_qm_dqrele(ip->i_gdquot);
> ip->i_gdquot = NULL;
> }
> + if ((flags & XFS_PQUOTA_ACCT) && ip->i_pdquot) {
> + xfs_qm_dqrele(ip->i_pdquot);
> + ip->i_pdquot = NULL;
> + }
> xfs_iunlock(ip, XFS_ILOCK_EXCL);
> return 0;
> }
> diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
> index ccff1a6..fe46c0c 100644
> --- a/fs/xfs/xfs_quota.h
> +++ b/fs/xfs/xfs_quota.h
> @@ -272,10 +272,10 @@ typedef struct xfs_qoff_logformat {
> * we didn't have the inode locked, the appropriate dquot(s) will be
> * attached atomically.
> */
> -#define XFS_NOT_DQATTACHED(mp, ip) ((XFS_IS_UQUOTA_ON(mp) &&\
> - (ip)->i_udquot == NULL) || \
> - (XFS_IS_OQUOTA_ON(mp) && \
> - (ip)->i_gdquot == NULL))
> +#define XFS_NOT_DQATTACHED(mp, ip) \
> + ((XFS_IS_UQUOTA_ON(mp) && (ip)->i_udquot == NULL) || \
> + (XFS_IS_GQUOTA_ON(mp) && (ip)->i_gdquot == NULL) || \
> + (XFS_IS_PQUOTA_ON(mp) && (ip)->i_pdquot == NULL))
>
> #define XFS_QM_NEED_QUOTACHECK(mp) \
> ((XFS_IS_UQUOTA_ON(mp) && \
> @@ -330,17 +330,18 @@ extern int xfs_trans_reserve_quota_nblks(struct xfs_trans *,
> struct xfs_inode *, long, long, uint);
> extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
> struct xfs_mount *, struct xfs_dquot *,
> - struct xfs_dquot *, long, long, uint);
> + struct xfs_dquot *, struct xfs_dquot *, long, long, uint);
>
> extern int xfs_qm_vop_dqalloc(struct xfs_inode *, uid_t, gid_t, prid_t, uint,
> - struct xfs_dquot **, struct xfs_dquot **);
> + struct xfs_dquot **, struct xfs_dquot **, struct xfs_dquot **);
> extern void xfs_qm_vop_create_dqattach(struct xfs_trans *, struct xfs_inode *,
> - struct xfs_dquot *, struct xfs_dquot *);
> + struct xfs_dquot *, struct xfs_dquot *, struct xfs_dquot *);
> extern int xfs_qm_vop_rename_dqattach(struct xfs_inode **);
> extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *,
> struct xfs_inode *, struct xfs_dquot **, struct xfs_dquot *);
> extern int xfs_qm_vop_chown_reserve(struct xfs_trans *, struct xfs_inode *,
> - struct xfs_dquot *, struct xfs_dquot *, uint);
> + struct xfs_dquot *, struct xfs_dquot *,
> + struct xfs_dquot *, uint);
> extern int xfs_qm_dqattach(struct xfs_inode *, uint);
> extern int xfs_qm_dqattach_locked(struct xfs_inode *, uint);
> extern void xfs_qm_dqdetach(struct xfs_inode *);
> @@ -354,10 +355,12 @@ extern void xfs_qm_unmount_quotas(struct xfs_mount *);
> #else
> static inline int
> xfs_qm_vop_dqalloc(struct xfs_inode *ip, uid_t uid, gid_t gid, prid_t prid,
> - uint flags, struct xfs_dquot **udqp, struct xfs_dquot **gdqp)
> + uint flags, struct xfs_dquot **udqp, struct xfs_dquot **gdqp,
> + struct xfs_dquot **pdqp)
> {
> *udqp = NULL;
> *gdqp = NULL;
> + *pdqp = NULL;
> return 0;
> }
> #define xfs_trans_dup_dqinfo(tp, tp2)
> @@ -372,14 +375,15 @@ static inline int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp,
> }
> static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
> struct xfs_mount *mp, struct xfs_dquot *udqp,
> - struct xfs_dquot *gdqp, long nblks, long nions, uint flags)
> + struct xfs_dquot *gdqp, struct xfs_dquot *pdqp,
> + long nblks, long nions, uint flags)
> {
> return 0;
> }
> -#define xfs_qm_vop_create_dqattach(tp, ip, u, g)
> +#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)
> -#define xfs_qm_vop_chown_reserve(tp, ip, u, g, fl) (0)
> +#define xfs_qm_vop_chown_reserve(tp, ip, u, g, p, fl) (0)
> #define xfs_qm_dqattach(ip, fl) (0)
> #define xfs_qm_dqattach_locked(ip, fl) (0)
> #define xfs_qm_dqdetach(ip)
> @@ -393,8 +397,8 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
>
> #define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \
> xfs_trans_reserve_quota_nblks(tp, ip, -(nblks), -(ninos), flags)
> -#define xfs_trans_reserve_quota(tp, mp, ud, gd, nb, ni, f) \
> - xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, nb, ni, \
> +#define xfs_trans_reserve_quota(tp, mp, ud, gd, pd, nb, ni, f) \
> + xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, pd, nb, ni, \
> f | XFS_QMOPT_RES_REGBLKS)
>
> extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 873fa5a..5feac04 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -560,14 +560,13 @@ xfs_showargs(
> else if (mp->m_qflags & XFS_UQUOTA_ACCT)
> seq_puts(m, "," MNTOPT_UQUOTANOENF);
>
> - /* Either project or group quotas can be active, not both */
> -
> if (mp->m_qflags & XFS_PQUOTA_ACCT) {
> if (mp->m_qflags & XFS_PQUOTA_ENFD)
> seq_puts(m, "," MNTOPT_PRJQUOTA);
> else
> seq_puts(m, "," MNTOPT_PQUOTANOENF);
> - } else if (mp->m_qflags & XFS_GQUOTA_ACCT) {
> + }
> + if (mp->m_qflags & XFS_GQUOTA_ACCT) {
> if (mp->m_qflags & XFS_GQUOTA_ENFD)
> seq_puts(m, "," MNTOPT_GRPQUOTA);
> else
> diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
> index 5f23438..d69e50a 100644
> --- a/fs/xfs/xfs_symlink.c
> +++ b/fs/xfs/xfs_symlink.c
> @@ -365,7 +365,9 @@ xfs_symlink(
> int n;
> xfs_buf_t *bp;
> prid_t prid;
> - struct xfs_dquot *udqp, *gdqp;
> + struct xfs_dquot *udqp = NULL;
> + struct xfs_dquot *gdqp = NULL;
> + struct xfs_dquot *pdqp = NULL;
> uint resblks;
>
> *ipp = NULL;
> @@ -392,7 +394,7 @@ xfs_symlink(
> * Make sure that we have allocated dquot(s) on disk.
> */
> error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
> - XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
> + XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp);
> if (error)
> goto std_return;
>
> @@ -433,7 +435,8 @@ xfs_symlink(
> /*
> * Reserve disk quota : blocks and inode.
> */
> - error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0);
> + error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
> + pdqp, resblks, 1, 0);
> if (error)
> goto error_return;
>
> @@ -471,7 +474,7 @@ xfs_symlink(
> /*
> * Also attach the dquot(s) to it, if applicable.
> */
> - xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp);
> + xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
>
> if (resblks)
> resblks -= XFS_IALLOC_SPACE_RES(mp);
> @@ -570,6 +573,7 @@ xfs_symlink(
> error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
> xfs_qm_dqrele(udqp);
> xfs_qm_dqrele(gdqp);
> + xfs_qm_dqrele(pdqp);
>
> *ipp = ip;
> return 0;
> @@ -583,6 +587,7 @@ xfs_symlink(
> xfs_trans_cancel(tp, cancel_flags);
> xfs_qm_dqrele(udqp);
> xfs_qm_dqrele(gdqp);
> + xfs_qm_dqrele(pdqp);
>
> if (unlock_dp_on_error)
> xfs_iunlock(dp, XFS_ILOCK_EXCL);
> diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
> index 8cdbd62..2bbad13 100644
> --- a/fs/xfs/xfs_trans_dquot.c
> +++ b/fs/xfs/xfs_trans_dquot.c
> @@ -103,8 +103,6 @@ xfs_trans_dup_dqinfo(
> return;
>
> xfs_trans_alloc_dqinfo(ntp);
> - oqa = otp->t_dqinfo->dqa_usrdquots;
> - nqa = ntp->t_dqinfo->dqa_usrdquots;
>
> /*
> * Because the quota blk reservation is carried forward,
> @@ -113,7 +111,10 @@ xfs_trans_dup_dqinfo(
> if(otp->t_flags & XFS_TRANS_DQ_DIRTY)
> ntp->t_flags |= XFS_TRANS_DQ_DIRTY;
>
> - for (j = 0; j < 2; j++) {
> + for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
> + oqa = otp->t_dqinfo->dqs[j];
> + nqa = ntp->t_dqinfo->dqs[j];
> +
> for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
> if (oqa[i].qt_dquot == NULL)
> break;
> @@ -138,8 +139,6 @@ xfs_trans_dup_dqinfo(
> oq->qt_ino_res = oq->qt_ino_res_used;
>
> }
> - oqa = otp->t_dqinfo->dqa_grpdquots;
> - nqa = ntp->t_dqinfo->dqa_grpdquots;
> }
> }
>
> @@ -166,8 +165,10 @@ xfs_trans_mod_dquot_byino(
>
> if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot)
> (void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta);
> - if (XFS_IS_OQUOTA_ON(mp) && ip->i_gdquot)
> + if (XFS_IS_GQUOTA_ON(mp) && ip->i_gdquot)
> (void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta);
> + if (XFS_IS_PQUOTA_ON(mp) && ip->i_pdquot)
> + (void) xfs_trans_mod_dquot(tp, ip->i_pdquot, field, delta);
> }
>
> STATIC xfs_dqtrx_t *
> @@ -178,15 +179,20 @@ xfs_trans_get_dqtrx(
> int i;
> xfs_dqtrx_t *qa;
>
> - qa = XFS_QM_ISUDQ(dqp) ?
> - tp->t_dqinfo->dqa_usrdquots : tp->t_dqinfo->dqa_grpdquots;
> + if (XFS_QM_ISUDQ(dqp))
> + qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR];
> + else if (XFS_QM_ISGDQ(dqp))
> + qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP];
> + else if (XFS_QM_ISPDQ(dqp))
> + qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_PROJ];
> + else
> + return NULL;
>
> for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
> if (qa[i].qt_dquot == NULL ||
> qa[i].qt_dquot == dqp)
> return &qa[i];
> }
> -
> return NULL;
> }
>
> @@ -339,12 +345,10 @@ xfs_trans_apply_dquot_deltas(
> return;
>
> ASSERT(tp->t_dqinfo);
> - qa = tp->t_dqinfo->dqa_usrdquots;
> - for (j = 0; j < 2; j++) {
> - if (qa[0].qt_dquot == NULL) {
> - qa = tp->t_dqinfo->dqa_grpdquots;
> + for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
> + qa = tp->t_dqinfo->dqs[j];
> + if (qa[0].qt_dquot == NULL)
> continue;
> - }
>
> /*
> * Lock all of the dquots and join them to the transaction.
> @@ -495,10 +499,6 @@ xfs_trans_apply_dquot_deltas(
> ASSERT(dqp->q_res_rtbcount >=
> be64_to_cpu(dqp->q_core.d_rtbcount));
> }
> - /*
> - * Do the group quotas next
> - */
> - qa = tp->t_dqinfo->dqa_grpdquots;
> }
> }
>
> @@ -521,9 +521,10 @@ xfs_trans_unreserve_and_mod_dquots(
> if (!tp->t_dqinfo || !(tp->t_flags & XFS_TRANS_DQ_DIRTY))
> return;
>
> - qa = tp->t_dqinfo->dqa_usrdquots;
>
> - for (j = 0; j < 2; j++) {
> + for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
> + qa = tp->t_dqinfo->dqs[j];
> +
> for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
> qtrx = &qa[i];
> /*
> @@ -565,7 +566,6 @@ xfs_trans_unreserve_and_mod_dquots(
> xfs_dqunlock(dqp);
>
> }
> - qa = tp->t_dqinfo->dqa_grpdquots;
> }
> }
>
> @@ -736,8 +736,8 @@ error_return:
>
> /*
> * Given dquot(s), make disk block and/or inode reservations against them.
> - * The fact that this does the reservation against both the usr and
> - * grp/prj quotas is important, because this follows a both-or-nothing
> + * The fact that this does the reservation against user, group and
> + * project quotas is important, because this follows a all-or-nothing
> * approach.
> *
> * flags = XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
> @@ -748,15 +748,16 @@ error_return:
> */
> int
> xfs_trans_reserve_quota_bydquots(
> - xfs_trans_t *tp,
> - xfs_mount_t *mp,
> - xfs_dquot_t *udqp,
> - xfs_dquot_t *gdqp,
> - long nblks,
> - long ninos,
> - uint flags)
> + struct xfs_trans *tp,
> + struct xfs_mount *mp,
> + struct xfs_dquot *udqp,
> + struct xfs_dquot *gdqp,
> + struct xfs_dquot *pdqp,
> + long nblks,
> + long ninos,
> + uint flags)
> {
> - int resvd = 0, error;
> + int error;
>
> if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
> return 0;
> @@ -771,28 +772,34 @@ xfs_trans_reserve_quota_bydquots(
> (flags & ~XFS_QMOPT_ENOSPC));
> if (error)
> return error;
> - resvd = 1;
> }
>
> if (gdqp) {
> error = xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags);
> - if (error) {
> - /*
> - * can't do it, so backout previous reservation
> - */
> - if (resvd) {
> - flags |= XFS_QMOPT_FORCE_RES;
> - xfs_trans_dqresv(tp, mp, udqp,
> - -nblks, -ninos, flags);
> - }
> - return error;
> - }
> + if (error)
> + goto unwind_usr;
> + }
> +
> + if (pdqp) {
> + error = xfs_trans_dqresv(tp, mp, pdqp, nblks, ninos, flags);
> + if (error)
> + goto unwind_grp;
> }
>
> /*
> * Didn't change anything critical, so, no need to log
> */
> return 0;
> +
> +unwind_grp:
> + flags |= XFS_QMOPT_FORCE_RES;
> + if (gdqp)
> + xfs_trans_dqresv(tp, mp, gdqp, -nblks, -ninos, flags);
> +unwind_usr:
> + flags |= XFS_QMOPT_FORCE_RES;
> + if (udqp)
> + xfs_trans_dqresv(tp, mp, udqp, -nblks, -ninos, flags);
> + return error;
> }
>
>
> @@ -830,6 +837,7 @@ xfs_trans_reserve_quota_nblks(
> */
> return xfs_trans_reserve_quota_bydquots(tp, mp,
> ip->i_udquot, ip->i_gdquot,
> + ip->i_pdquot,
> nblks, ninos, flags);
> }
>
> diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
> index 1501f4f..cd0d133 100644
> --- a/fs/xfs/xfs_vnodeops.c
> +++ b/fs/xfs/xfs_vnodeops.c
> @@ -498,6 +498,7 @@ xfs_create(
> prid_t prid;
> struct xfs_dquot *udqp = NULL;
> struct xfs_dquot *gdqp = NULL;
> + struct xfs_dquot *pdqp = NULL;
> uint resblks;
> uint log_res;
> uint log_count;
> @@ -516,7 +517,7 @@ xfs_create(
> * Make sure that we have allocated dquot(s) on disk.
> */
> error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
> - XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
> + XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp);
> if (error)
> return error;
>
> @@ -568,7 +569,8 @@ xfs_create(
> /*
> * Reserve disk quota and the inode.
> */
> - error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0);
> + error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
> + pdqp, resblks, 1, 0);
> if (error)
> goto out_trans_cancel;
>
> @@ -632,7 +634,7 @@ xfs_create(
> * These ids of the inode couldn't have changed since the new
> * inode has been locked ever since it was created.
> */
> - xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp);
> + xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
>
> error = xfs_bmap_finish(&tp, &free_list, &committed);
> if (error)
> @@ -644,6 +646,7 @@ xfs_create(
>
> xfs_qm_dqrele(udqp);
> xfs_qm_dqrele(gdqp);
> + xfs_qm_dqrele(pdqp);
>
> *ipp = ip;
> return 0;
> @@ -665,6 +668,7 @@ xfs_create(
>
> xfs_qm_dqrele(udqp);
> xfs_qm_dqrele(gdqp);
> + xfs_qm_dqrele(pdqp);
>
> if (unlock_dp_on_error)
> xfs_iunlock(dp, XFS_ILOCK_EXCL);
> @@ -1577,7 +1581,7 @@ xfs_free_file_space(
> }
> xfs_ilock(ip, XFS_ILOCK_EXCL);
> error = xfs_trans_reserve_quota(tp, mp,
> - ip->i_udquot, ip->i_gdquot,
> + ip->i_udquot, ip->i_gdquot, ip->i_pdquot,
> resblks, 0, XFS_QMOPT_RES_REGBLKS);
> if (error)
> goto error1;
>
Thanks,
-Jeff