drm/rockchip: vop: enable iommu when we actually need it
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_qm.c
index b75c9bb6e71e34b0c65158f63ba475a217d9e347..532ab79d38fe376c14a5463a97195b59a61d8f84 100644 (file)
  */
 #include "xfs.h"
 #include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
 #include "xfs_bit.h"
-#include "xfs_log.h"
-#include "xfs_trans.h"
 #include "xfs_sb.h"
-#include "xfs_ag.h"
-#include "xfs_alloc.h"
-#include "xfs_quota.h"
 #include "xfs_mount.h"
-#include "xfs_bmap_btree.h"
-#include "xfs_ialloc_btree.h"
-#include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_ialloc.h"
 #include "xfs_itable.h"
-#include "xfs_rtalloc.h"
+#include "xfs_quota.h"
 #include "xfs_error.h"
 #include "xfs_bmap.h"
-#include "xfs_attr.h"
-#include "xfs_buf_item.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans.h"
 #include "xfs_trans_space.h"
-#include "xfs_utils.h"
 #include "xfs_qm.h"
 #include "xfs_trace.h"
 #include "xfs_icache.h"
@@ -51,8 +46,9 @@
  */
 STATIC int     xfs_qm_init_quotainos(xfs_mount_t *);
 STATIC int     xfs_qm_init_quotainfo(xfs_mount_t *);
-STATIC int     xfs_qm_shake(struct shrinker *, struct shrink_control *);
 
+
+STATIC void    xfs_qm_dqfree_one(struct xfs_dquot *dqp);
 /*
  * We use the batch lookup interface to iterate over the dquots as it
  * currently is the only interface into the radix tree code that allows
@@ -70,7 +66,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;
@@ -100,18 +96,18 @@ restart:
                        next_index = be32_to_cpu(dqp->q_core.d_id) + 1;
 
                        error = execute(batch[i], data);
-                       if (error == EAGAIN) {
+                       if (error == -EAGAIN) {
                                skipped++;
                                continue;
                        }
-                       if (error && last_error != EFSCORRUPTED)
+                       if (error && last_error != -EFSCORRUPTED)
                                last_error = error;
                }
 
                mutex_unlock(&qi->qi_tree_lock);
 
                /* bail out if the filesystem is corrupted.  */
-               if (last_error == EFSCORRUPTED) {
+               if (last_error == -EFSCORRUPTED) {
                        skipped = 0;
                        break;
                }
@@ -136,22 +132,11 @@ xfs_qm_dqpurge(
 {
        struct xfs_mount        *mp = dqp->q_mount;
        struct xfs_quotainfo    *qi = mp->m_quotainfo;
-       struct xfs_dquot        *gdqp = NULL;
 
        xfs_dqlock(dqp);
        if ((dqp->dq_flags & XFS_DQ_FREEING) || dqp->q_nrefs != 0) {
                xfs_dqunlock(dqp);
-               return EAGAIN;
-       }
-
-       /*
-        * If this quota has a group hint attached, prepare for releasing it
-        * now.
-        */
-       gdqp = dqp->q_gdquot;
-       if (gdqp) {
-               xfs_dqlock(gdqp);
-               dqp->q_gdquot = NULL;
+               return -EAGAIN;
        }
 
        dqp->dq_flags |= XFS_DQ_FREEING;
@@ -189,7 +174,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--;
 
@@ -197,17 +182,11 @@ xfs_qm_dqpurge(
         * We move dquots to the freelist as soon as their reference count
         * hits zero, so it really should be on the freelist here.
         */
-       mutex_lock(&qi->qi_lru_lock);
        ASSERT(!list_empty(&dqp->q_lru));
-       list_del_init(&dqp->q_lru);
-       qi->qi_lru_count--;
-       XFS_STATS_DEC(xs_qm_dquot_unused);
-       mutex_unlock(&qi->qi_lru_lock);
+       list_lru_del(&qi->qi_lru, &dqp->q_lru);
+       XFS_STATS_DEC(mp, xs_qm_dquot_unused);
 
        xfs_qm_dqdestroy(dqp);
-
-       if (gdqp)
-               xfs_qm_dqput(gdqp);
        return 0;
 }
 
@@ -240,98 +219,6 @@ xfs_qm_unmount(
        }
 }
 
-
-/*
- * This is called from xfs_mountfs to start quotas and initialize all
- * necessary data structures like quotainfo.  This is also responsible for
- * running a quotacheck as necessary.  We are guaranteed that the superblock
- * is consistently read in at this point.
- *
- * If we fail here, the mount will continue with quota turned off. We don't
- * need to inidicate success or failure at all.
- */
-void
-xfs_qm_mount_quotas(
-       xfs_mount_t     *mp)
-{
-       int             error = 0;
-       uint            sbf;
-
-       /*
-        * If quotas on realtime volumes is not supported, we disable
-        * quotas immediately.
-        */
-       if (mp->m_sb.sb_rextents) {
-               xfs_notice(mp, "Cannot turn on quotas for realtime filesystem");
-               mp->m_qflags = 0;
-               goto write_changes;
-       }
-
-       ASSERT(XFS_IS_QUOTA_RUNNING(mp));
-
-       /*
-        * Allocate the quotainfo structure inside the mount struct, and
-        * create quotainode(s), and change/rev superblock if necessary.
-        */
-       error = xfs_qm_init_quotainfo(mp);
-       if (error) {
-               /*
-                * We must turn off quotas.
-                */
-               ASSERT(mp->m_quotainfo == NULL);
-               mp->m_qflags = 0;
-               goto write_changes;
-       }
-       /*
-        * If any of the quotas are not consistent, do a quotacheck.
-        */
-       if (XFS_QM_NEED_QUOTACHECK(mp)) {
-               error = xfs_qm_quotacheck(mp);
-               if (error) {
-                       /* Quotacheck failed and disabled quotas. */
-                       return;
-               }
-       }
-       /* 
-        * If one type of quotas is off, then it will lose its
-        * quotachecked status, since we won't be doing accounting for
-        * that type anymore.
-        */
-       if (!XFS_IS_UQUOTA_ON(mp))
-               mp->m_qflags &= ~XFS_UQUOTA_CHKD;
-       if (!(XFS_IS_GQUOTA_ON(mp) || XFS_IS_PQUOTA_ON(mp)))
-               mp->m_qflags &= ~XFS_OQUOTA_CHKD;
-
- write_changes:
-       /*
-        * We actually don't have to acquire the m_sb_lock at all.
-        * This can only be called from mount, and that's single threaded. XXX
-        */
-       spin_lock(&mp->m_sb_lock);
-       sbf = mp->m_sb.sb_qflags;
-       mp->m_sb.sb_qflags = mp->m_qflags & XFS_MOUNT_QUOTA_ALL;
-       spin_unlock(&mp->m_sb_lock);
-
-       if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) {
-               if (xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS)) {
-                       /*
-                        * We could only have been turning quotas off.
-                        * We aren't in very good shape actually because
-                        * the incore structures are convinced that quotas are
-                        * off, but the on disk superblock doesn't know that !
-                        */
-                       ASSERT(!(XFS_IS_QUOTA_RUNNING(mp)));
-                       xfs_alert(mp, "%s: Superblock update failed!",
-                               __func__);
-               }
-       }
-
-       if (error) {
-               xfs_warn(mp, "Failed to initialize disk quotas.");
-               return;
-       }
-}
-
 /*
  * Called from the vfsops layer.
  */
@@ -362,6 +249,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;
+               }
        }
 }
 
@@ -371,7 +262,6 @@ xfs_qm_dqattach_one(
        xfs_dqid_t      id,
        uint            type,
        uint            doalloc,
-       xfs_dquot_t     *udqhint, /* hint */
        xfs_dquot_t     **IO_idqpp)
 {
        xfs_dquot_t     *dqp;
@@ -381,9 +271,9 @@ xfs_qm_dqattach_one(
        error = 0;
 
        /*
-        * See if we already have it in the inode itself. IO_idqpp is
-        * &i_udquot or &i_gdquot. This made the code look weird, but
-        * made the logic a lot simpler.
+        * See if we already have it in the inode itself. IO_idqpp is &i_udquot
+        * or &i_gdquot. This made the code look weird, but made the logic a lot
+        * simpler.
         */
        dqp = *IO_idqpp;
        if (dqp) {
@@ -392,46 +282,10 @@ xfs_qm_dqattach_one(
        }
 
        /*
-        * udqhint is the i_udquot field in inode, and is non-NULL only
-        * when the type arg is group/project. Its purpose is to save a
-        * lookup by dqid (xfs_qm_dqget) by caching a group dquot inside
-        * the user dquot.
-        */
-       if (udqhint) {
-               ASSERT(type == XFS_DQ_GROUP || type == XFS_DQ_PROJ);
-               xfs_dqlock(udqhint);
-
-               /*
-                * No need to take dqlock to look at the id.
-                *
-                * The ID can't change until it gets reclaimed, and it won't
-                * be reclaimed as long as we have a ref from inode and we
-                * hold the ilock.
-                */
-               dqp = udqhint->q_gdquot;
-               if (dqp && be32_to_cpu(dqp->q_core.d_id) == id) {
-                       ASSERT(*IO_idqpp == NULL);
-
-                       *IO_idqpp = xfs_qm_dqhold(dqp);
-                       xfs_dqunlock(udqhint);
-                       return 0;
-               }
-
-               /*
-                * We can't hold a dquot lock when we call the dqget code.
-                * We'll deadlock in no time, because of (not conforming to)
-                * lock ordering - the inodelock comes before any dquot lock,
-                * and we may drop and reacquire the ilock in xfs_qm_dqget().
-                */
-               xfs_dqunlock(udqhint);
-       }
-
-       /*
-        * Find the dquot from somewhere. This bumps the
-        * reference count of dquot and returns it locked.
-        * This can return ENOENT if dquot didn't exist on
-        * disk and we didn't ask it to allocate;
-        * ESRCH if quotas got turned off suddenly.
+        * Find the dquot from somewhere. This bumps the reference count of
+        * dquot and returns it locked.  This can return ENOENT if dquot didn't
+        * exist on disk and we didn't ask it to allocate; ESRCH if quotas got
+        * turned off suddenly.
         */
        error = xfs_qm_dqget(ip->i_mount, ip, id, type,
                             doalloc | XFS_QMOPT_DOWARN, &dqp);
@@ -449,34 +303,6 @@ xfs_qm_dqattach_one(
        return 0;
 }
 
-
-/*
- * Given a udquot and gdquot, attach a ptr to the group 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_dquot_t     *tmp;
-
-       xfs_dqlock(udq);
-
-       tmp = udq->q_gdquot;
-       if (tmp) {
-               if (tmp == gdq)
-                       goto done;
-
-               udq->q_gdquot = NULL;
-               xfs_qm_dqrele(tmp);
-       }
-
-       udq->q_gdquot = xfs_qm_dqhold(gdq);
-done:
-       xfs_dqunlock(udq);
-}
-
 static bool
 xfs_qm_need_dqattach(
        struct xfs_inode        *ip)
@@ -489,8 +315,7 @@ xfs_qm_need_dqattach(
                return false;
        if (!XFS_NOT_DQATTACHED(mp, ip))
                return false;
-       if (ip->i_ino == mp->m_sb.sb_uquotino ||
-           ip->i_ino == mp->m_sb.sb_gquotino)
+       if (xfs_is_quota_inode(&mp->m_sb, ip->i_ino))
                return false;
        return true;
 }
@@ -508,7 +333,6 @@ xfs_qm_dqattach_locked(
        uint            flags)
 {
        xfs_mount_t     *mp = ip->i_mount;
-       uint            nquotas = 0;
        int             error = 0;
 
        if (!xfs_qm_need_dqattach(ip))
@@ -516,62 +340,39 @@ xfs_qm_dqattach_locked(
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 
-       if (XFS_IS_UQUOTA_ON(mp)) {
+       if (XFS_IS_UQUOTA_ON(mp) && !ip->i_udquot) {
                error = xfs_qm_dqattach_one(ip, ip->i_d.di_uid, XFS_DQ_USER,
                                                flags & XFS_QMOPT_DQALLOC,
-                                               NULL, &ip->i_udquot);
+                                               &ip->i_udquot);
                if (error)
                        goto done;
-               nquotas++;
+               ASSERT(ip->i_udquot);
        }
 
-       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,
+       if (XFS_IS_GQUOTA_ON(mp) && !ip->i_gdquot) {
+               error = 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,
+                                               &ip->i_gdquot);
+               if (error)
+                       goto done;
+               ASSERT(ip->i_gdquot);
+       }
+
+       if (XFS_IS_PQUOTA_ON(mp) && !ip->i_pdquot) {
+               error = xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
                                                flags & XFS_QMOPT_DQALLOC,
-                                               ip->i_udquot, &ip->i_gdquot);
-               /*
-                * Don't worry about the udquot that we may have
-                * attached above. It'll get detached, if not already.
-                */
+                                               &ip->i_pdquot);
                if (error)
                        goto done;
-               nquotas++;
+               ASSERT(ip->i_pdquot);
        }
 
+done:
        /*
-        * Attach this group quota to the user quota as a hint.
-        * This WON'T, in general, result in a thrash.
+        * Don't worry about the dquots that we may have attached before any
+        * error - they'll get detached later if it has not already been done.
         */
-       if (nquotas == 2) {
-               ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-               ASSERT(ip->i_udquot);
-               ASSERT(ip->i_gdquot);
-
-               /*
-                * We do not have i_udquot locked at this point, but this check
-                * is OK since we don't depend on the i_gdquot to be accurate
-                * 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);
-       }
-
- done:
-#ifdef DEBUG
-       if (!error) {
-               if (XFS_IS_UQUOTA_ON(mp))
-                       ASSERT(ip->i_udquot);
-               if (XFS_IS_OQUOTA_ON(mp))
-                       ASSERT(ip->i_gdquot);
-       }
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-#endif
        return error;
 }
 
@@ -601,13 +402,12 @@ 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);
 
-       ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_uquotino);
-       ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_gquotino);
+       ASSERT(!xfs_is_quota_inode(&ip->i_mount->m_sb, ip->i_ino));
        if (ip->i_udquot) {
                xfs_qm_dqrele(ip->i_udquot);
                ip->i_udquot = NULL;
@@ -616,20 +416,148 @@ 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
-xfs_qm_calc_dquots_per_chunk(
-       struct xfs_mount        *mp,
-       unsigned int            nbblks) /* basic block units */
+struct xfs_qm_isolate {
+       struct list_head        buffers;
+       struct list_head        dispose;
+};
+
+static enum lru_status
+xfs_qm_dquot_isolate(
+       struct list_head        *item,
+       struct list_lru_one     *lru,
+       spinlock_t              *lru_lock,
+       void                    *arg)
+               __releases(lru_lock) __acquires(lru_lock)
 {
-       unsigned int    ndquots;
+       struct xfs_dquot        *dqp = container_of(item,
+                                               struct xfs_dquot, q_lru);
+       struct xfs_qm_isolate   *isol = arg;
+
+       if (!xfs_dqlock_nowait(dqp))
+               goto out_miss_busy;
+
+       /*
+        * This dquot has acquired a reference in the meantime remove it from
+        * the freelist and try again.
+        */
+       if (dqp->q_nrefs) {
+               xfs_dqunlock(dqp);
+               XFS_STATS_INC(dqp->q_mount, xs_qm_dqwants);
+
+               trace_xfs_dqreclaim_want(dqp);
+               list_lru_isolate(lru, &dqp->q_lru);
+               XFS_STATS_DEC(dqp->q_mount, xs_qm_dquot_unused);
+               return LRU_REMOVED;
+       }
+
+       /*
+        * If the dquot is dirty, flush it. If it's already being flushed, just
+        * skip it so there is time for the IO to complete before we try to
+        * reclaim it again on the next LRU pass.
+        */
+       if (!xfs_dqflock_nowait(dqp)) {
+               xfs_dqunlock(dqp);
+               goto out_miss_busy;
+       }
+
+       if (XFS_DQ_IS_DIRTY(dqp)) {
+               struct xfs_buf  *bp = NULL;
+               int             error;
+
+               trace_xfs_dqreclaim_dirty(dqp);
+
+               /* we have to drop the LRU lock to flush the dquot */
+               spin_unlock(lru_lock);
+
+               error = xfs_qm_dqflush(dqp, &bp);
+               if (error) {
+                       xfs_warn(dqp->q_mount, "%s: dquot %p flush failed",
+                                __func__, dqp);
+                       goto out_unlock_dirty;
+               }
+
+               xfs_buf_delwri_queue(bp, &isol->buffers);
+               xfs_buf_relse(bp);
+               goto out_unlock_dirty;
+       }
+       xfs_dqfunlock(dqp);
 
-       ASSERT(nbblks > 0);
-       ndquots = BBTOB(nbblks);
-       do_div(ndquots, sizeof(xfs_dqblk_t));
+       /*
+        * Prevent lookups now that we are past the point of no return.
+        */
+       dqp->dq_flags |= XFS_DQ_FREEING;
+       xfs_dqunlock(dqp);
 
-       return ndquots;
+       ASSERT(dqp->q_nrefs == 0);
+       list_lru_isolate_move(lru, &dqp->q_lru, &isol->dispose);
+       XFS_STATS_DEC(dqp->q_mount, xs_qm_dquot_unused);
+       trace_xfs_dqreclaim_done(dqp);
+       XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaims);
+       return LRU_REMOVED;
+
+out_miss_busy:
+       trace_xfs_dqreclaim_busy(dqp);
+       XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaim_misses);
+       return LRU_SKIP;
+
+out_unlock_dirty:
+       trace_xfs_dqreclaim_busy(dqp);
+       XFS_STATS_INC(dqp->q_mount, xs_qm_dqreclaim_misses);
+       xfs_dqunlock(dqp);
+       spin_lock(lru_lock);
+       return LRU_RETRY;
+}
+
+static unsigned long
+xfs_qm_shrink_scan(
+       struct shrinker         *shrink,
+       struct shrink_control   *sc)
+{
+       struct xfs_quotainfo    *qi = container_of(shrink,
+                                       struct xfs_quotainfo, qi_shrinker);
+       struct xfs_qm_isolate   isol;
+       unsigned long           freed;
+       int                     error;
+
+       if ((sc->gfp_mask & (__GFP_FS|__GFP_DIRECT_RECLAIM)) != (__GFP_FS|__GFP_DIRECT_RECLAIM))
+               return 0;
+
+       INIT_LIST_HEAD(&isol.buffers);
+       INIT_LIST_HEAD(&isol.dispose);
+
+       freed = list_lru_shrink_walk(&qi->qi_lru, sc,
+                                    xfs_qm_dquot_isolate, &isol);
+
+       error = xfs_buf_delwri_submit(&isol.buffers);
+       if (error)
+               xfs_warn(NULL, "%s: dquot reclaim failed", __func__);
+
+       while (!list_empty(&isol.dispose)) {
+               struct xfs_dquot        *dqp;
+
+               dqp = list_first_entry(&isol.dispose, struct xfs_dquot, q_lru);
+               list_del_init(&dqp->q_lru);
+               xfs_qm_dqfree_one(dqp);
+       }
+
+       return freed;
+}
+
+static unsigned long
+xfs_qm_shrink_count(
+       struct shrinker         *shrink,
+       struct shrink_control   *sc)
+{
+       struct xfs_quotainfo    *qi = container_of(shrink,
+                                       struct xfs_quotainfo, qi_shrinker);
+
+       return list_lru_shrink_count(&qi->qi_lru, sc);
 }
 
 /*
@@ -648,31 +576,29 @@ xfs_qm_init_quotainfo(
 
        qinf = mp->m_quotainfo = kmem_zalloc(sizeof(xfs_quotainfo_t), KM_SLEEP);
 
+       error = list_lru_init(&qinf->qi_lru);
+       if (error)
+               goto out_free_qinf;
+
        /*
         * See if quotainodes are setup, and if not, allocate them,
         * and change the superblock accordingly.
         */
-       if ((error = xfs_qm_init_quotainos(mp))) {
-               kmem_free(qinf);
-               mp->m_quotainfo = NULL;
-               return error;
-       }
+       error = xfs_qm_init_quotainos(mp);
+       if (error)
+               goto out_free_lru;
 
        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);
-       qinf->qi_lru_count = 0;
-       mutex_init(&qinf->qi_lru_lock);
-
        /* mutex used to serialize quotaoffs */
        mutex_init(&qinf->qi_quotaofflock);
 
        /* Precalc some constants */
        qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
-       qinf->qi_dqperchunk = xfs_qm_calc_dquots_per_chunk(mp,
-                                                       qinf->qi_dqchunklen);
+       qinf->qi_dqperchunk = xfs_calc_dquots_per_chunk(qinf->qi_dqchunklen);
 
        mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);
 
@@ -719,7 +645,7 @@ xfs_qm_init_quotainfo(
                qinf->qi_isoftlimit = be64_to_cpu(ddqp->d_ino_softlimit);
                qinf->qi_rtbhardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit);
                qinf->qi_rtbsoftlimit = be64_to_cpu(ddqp->d_rtb_softlimit);
+
                xfs_qm_dqdestroy(dqp);
        } else {
                qinf->qi_btimelimit = XFS_QM_BTIMELIMIT;
@@ -730,10 +656,19 @@ xfs_qm_init_quotainfo(
                qinf->qi_rtbwarnlimit = XFS_QM_RTBWARNLIMIT;
        }
 
-       qinf->qi_shrinker.shrink = xfs_qm_shake;
+       qinf->qi_shrinker.count_objects = xfs_qm_shrink_count;
+       qinf->qi_shrinker.scan_objects = xfs_qm_shrink_scan;
        qinf->qi_shrinker.seeks = DEFAULT_SEEKS;
+       qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
        register_shrinker(&qinf->qi_shrinker);
        return 0;
+
+out_free_lru:
+       list_lru_destroy(&qinf->qi_lru);
+out_free_qinf:
+       kmem_free(qinf);
+       mp->m_quotainfo = NULL;
+       return error;
 }
 
 
@@ -752,6 +687,7 @@ xfs_qm_destroy_quotainfo(
        ASSERT(qi != NULL);
 
        unregister_shrinker(&qi->qi_shrinker);
+       list_lru_destroy(&qi->qi_lru);
 
        if (qi->qi_uquotaip) {
                IRELE(qi->qi_uquotaip);
@@ -761,6 +697,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;
@@ -774,30 +714,61 @@ STATIC int
 xfs_qm_qino_alloc(
        xfs_mount_t     *mp,
        xfs_inode_t     **ip,
-       __int64_t       sbfields,
        uint            flags)
 {
        xfs_trans_t     *tp;
        int             error;
        int             committed;
+       bool            need_alloc = true;
 
-       tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QINOCREATE);
-       if ((error = xfs_trans_reserve(tp,
-                                     XFS_QM_QINOCREATE_SPACE_RES(mp),
-                                     XFS_CREATE_LOG_RES(mp), 0,
-                                     XFS_TRANS_PERM_LOG_RES,
-                                     XFS_CREATE_LOG_COUNT))) {
-               xfs_trans_cancel(tp, 0);
-               return error;
+       *ip = NULL;
+       /*
+        * With superblock that doesn't have separate pquotino, we
+        * share an inode between gquota and pquota. If the on-disk
+        * superblock has GQUOTA and the filesystem is now mounted
+        * with PQUOTA, just use sb_gquotino for sb_pquotino and
+        * vice-versa.
+        */
+       if (!xfs_sb_version_has_pquotino(&mp->m_sb) &&
+                       (flags & (XFS_QMOPT_PQUOTA|XFS_QMOPT_GQUOTA))) {
+               xfs_ino_t ino = NULLFSINO;
+
+               if ((flags & XFS_QMOPT_PQUOTA) &&
+                            (mp->m_sb.sb_gquotino != NULLFSINO)) {
+                       ino = mp->m_sb.sb_gquotino;
+                       ASSERT(mp->m_sb.sb_pquotino == NULLFSINO);
+               } else if ((flags & XFS_QMOPT_GQUOTA) &&
+                            (mp->m_sb.sb_pquotino != NULLFSINO)) {
+                       ino = mp->m_sb.sb_pquotino;
+                       ASSERT(mp->m_sb.sb_gquotino == NULLFSINO);
+               }
+               if (ino != NULLFSINO) {
+                       error = xfs_iget(mp, NULL, ino, 0, 0, ip);
+                       if (error)
+                               return error;
+                       mp->m_sb.sb_gquotino = NULLFSINO;
+                       mp->m_sb.sb_pquotino = NULLFSINO;
+                       need_alloc = false;
+               }
        }
 
-       error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, 0, 1, ip, &committed);
+       tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QINOCREATE);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_create,
+                                 XFS_QM_QINOCREATE_SPACE_RES(mp), 0);
        if (error) {
-               xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |
-                                XFS_TRANS_ABORT);
+               xfs_trans_cancel(tp);
                return error;
        }
 
+       if (need_alloc) {
+               error = xfs_dir_ialloc(&tp, NULL, S_IFREG, 1, 0, 0, 1, ip,
+                                                               &committed);
+               if (error) {
+                       xfs_trans_cancel(tp);
+                       return error;
+               }
+       }
+
        /*
         * Make the changes in the superblock, and log those too.
         * sbfields arg may contain fields other than *QUOTINO;
@@ -806,30 +777,32 @@ xfs_qm_qino_alloc(
        spin_lock(&mp->m_sb_lock);
        if (flags & XFS_QMOPT_SBVERSION) {
                ASSERT(!xfs_sb_version_hasquota(&mp->m_sb));
-               ASSERT((sbfields & (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
-                                  XFS_SB_GQUOTINO | XFS_SB_QFLAGS)) ==
-                      (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
-                       XFS_SB_GQUOTINO | XFS_SB_QFLAGS));
 
                xfs_sb_version_addquota(&mp->m_sb);
                mp->m_sb.sb_uquotino = NULLFSINO;
                mp->m_sb.sb_gquotino = NULLFSINO;
+               mp->m_sb.sb_pquotino = NULLFSINO;
 
-               /* qflags will get updated _after_ quotacheck */
-               mp->m_sb.sb_qflags = 0;
+               /* qflags will get updated fully _after_ quotacheck */
+               mp->m_sb.sb_qflags = mp->m_qflags & XFS_ALL_QUOTA_ACCT;
        }
        if (flags & XFS_QMOPT_UQUOTA)
                mp->m_sb.sb_uquotino = (*ip)->i_ino;
-       else
+       else if (flags & XFS_QMOPT_GQUOTA)
                mp->m_sb.sb_gquotino = (*ip)->i_ino;
+       else
+               mp->m_sb.sb_pquotino = (*ip)->i_ino;
        spin_unlock(&mp->m_sb_lock);
-       xfs_mod_sb(tp, sbfields);
+       xfs_log_sb(tp);
 
-       if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES))) {
+       error = xfs_trans_commit(tp);
+       if (error) {
+               ASSERT(XFS_FORCED_SHUTDOWN(mp));
                xfs_alert(mp, "%s failed (error %d)!", __func__, error);
-               return error;
        }
-       return 0;
+       if (need_alloc)
+               xfs_finish_inode_setup(*ip);
+       return error;
 }
 
 
@@ -863,10 +836,15 @@ xfs_qm_reset_dqcounts(
                /*
                 * Do a sanity check, and if needed, repair the dqblk. Don't
                 * output any warnings because it's perfectly possible to
-                * find uninitialised dquot blks. See comment in xfs_qm_dqcheck.
+                * find uninitialised dquot blks. See comment in xfs_dqcheck.
                 */
-               (void) xfs_qm_dqcheck(mp, ddq, id+j, type, XFS_QMOPT_DQREPAIR,
-                                     "xfs_quotacheck");
+               xfs_dqcheck(mp, ddq, id+j, type, XFS_QMOPT_DQREPAIR,
+                           "xfs_quotacheck");
+               /*
+                * Reset type in case we are reusing group quota file for
+                * project quotas or vice versa
+                */
+               ddq->d_flags = type;
                ddq->d_bcount = 0;
                ddq->d_icount = 0;
                ddq->d_rtbcount = 0;
@@ -925,7 +903,7 @@ xfs_qm_dqiter_bufs(
                 * will leave a trace in the log indicating corruption has
                 * been detected.
                 */
-               if (error == EFSCORRUPTED) {
+               if (error == -EFSCORRUPTED) {
                        error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
                                      XFS_FSB_TO_DADDR(mp, bno),
                                      mp->m_quotainfo->qi_dqchunklen, 0, &bp,
@@ -935,6 +913,12 @@ xfs_qm_dqiter_bufs(
                if (error)
                        break;
 
+               /*
+                * A corrupt buffer might not have a verifier attached, so
+                * make sure we have the correct one attached before writeback
+                * occurs.
+                */
+               bp->b_ops = &xfs_dquot_buf_ops;
                xfs_qm_reset_dqcounts(mp, bp, firstid, type);
                xfs_buf_delwri_queue(bp, buffer_list);
                xfs_buf_relse(bp);
@@ -981,16 +965,18 @@ xfs_qm_dqiterate(
        lblkno = 0;
        maxlblkcnt = XFS_B_TO_FSB(mp, mp->m_super->s_maxbytes);
        do {
+               uint            lock_mode;
+
                nmaps = XFS_DQITER_MAP_SIZE;
                /*
                 * We aren't changing the inode itself. Just changing
                 * some of its data. No new blocks are added here, and
                 * the inode is never added to the transaction.
                 */
-               xfs_ilock(qip, XFS_ILOCK_SHARED);
+               lock_mode = xfs_ilock_data_map_shared(qip);
                error = xfs_bmapi_read(qip, lblkno, maxlblkcnt - lblkno,
                                       map, &nmaps, 0);
-               xfs_iunlock(qip, XFS_ILOCK_SHARED);
+               xfs_iunlock(qip, lock_mode);
                if (error)
                        break;
 
@@ -1018,7 +1004,7 @@ xfs_qm_dqiterate(
                                        xfs_buf_readahead(mp->m_ddev_targp,
                                               XFS_FSB_TO_DADDR(mp, rablkno),
                                               mp->m_quotainfo->qi_dqchunklen,
-                                              NULL);
+                                              &xfs_dquot_buf_ops);
                                        rablkno++;
                                }
                        }
@@ -1066,8 +1052,8 @@ xfs_qm_quotacheck_dqadjust(
                /*
                 * Shouldn't be able to turn off quotas here.
                 */
-               ASSERT(error != ESRCH);
-               ASSERT(error != ENOENT);
+               ASSERT(error != -ESRCH);
+               ASSERT(error != -ENOENT);
                return error;
        }
 
@@ -1152,9 +1138,9 @@ xfs_qm_dqusage_adjust(
         * rootino must have its resources accounted for, not so with the quota
         * inodes.
         */
-       if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) {
+       if (xfs_is_quota_inode(&mp->m_sb, ino)) {
                *res = BULKSTAT_RV_NOTHING;
-               return XFS_ERROR(EINVAL);
+               return -EINVAL;
        }
 
        /*
@@ -1258,23 +1244,25 @@ out_unlock:
  * Walk thru all the filesystem inodes and construct a consistent view
  * of the disk quota world. If the quotacheck fails, disable quotas.
  */
-int
+STATIC int
 xfs_qm_quotacheck(
        xfs_mount_t     *mp)
 {
-       int             done, count, error, error2;
-       xfs_ino_t       lastino;
-       size_t          structsz;
-       xfs_inode_t     *uip, *gip;
-       uint            flags;
-       LIST_HEAD       (buffer_list);
+       int                     done, count, error, error2;
+       xfs_ino_t               lastino;
+       size_t                  structsz;
+       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.");
@@ -1284,7 +1272,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);
@@ -1293,14 +1280,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_OQUOTA_CHKD;
+               flags |= XFS_GQUOTA_CHKD;
+       }
+
+       if (pip) {
+               error = xfs_qm_dqiterate(mp, pip, XFS_QMOPT_PQUOTA,
+                                        &buffer_list);
+               if (error)
+                       goto error_return;
+               flags |= XFS_PQUOTA_CHKD;
        }
 
        do {
@@ -1384,7 +1377,100 @@ xfs_qm_quotacheck(
                }
        } else
                xfs_notice(mp, "Quotacheck: Done.");
-       return (error);
+       return error;
+}
+
+/*
+ * This is called from xfs_mountfs to start quotas and initialize all
+ * necessary data structures like quotainfo.  This is also responsible for
+ * running a quotacheck as necessary.  We are guaranteed that the superblock
+ * is consistently read in at this point.
+ *
+ * If we fail here, the mount will continue with quota turned off. We don't
+ * need to inidicate success or failure at all.
+ */
+void
+xfs_qm_mount_quotas(
+       struct xfs_mount        *mp)
+{
+       int                     error = 0;
+       uint                    sbf;
+
+       /*
+        * If quotas on realtime volumes is not supported, we disable
+        * quotas immediately.
+        */
+       if (mp->m_sb.sb_rextents) {
+               xfs_notice(mp, "Cannot turn on quotas for realtime filesystem");
+               mp->m_qflags = 0;
+               goto write_changes;
+       }
+
+       ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+
+       /*
+        * Allocate the quotainfo structure inside the mount struct, and
+        * create quotainode(s), and change/rev superblock if necessary.
+        */
+       error = xfs_qm_init_quotainfo(mp);
+       if (error) {
+               /*
+                * We must turn off quotas.
+                */
+               ASSERT(mp->m_quotainfo == NULL);
+               mp->m_qflags = 0;
+               goto write_changes;
+       }
+       /*
+        * If any of the quotas are not consistent, do a quotacheck.
+        */
+       if (XFS_QM_NEED_QUOTACHECK(mp)) {
+               error = xfs_qm_quotacheck(mp);
+               if (error) {
+                       /* Quotacheck failed and disabled quotas. */
+                       return;
+               }
+       }
+       /*
+        * If one type of quotas is off, then it will lose its
+        * quotachecked status, since we won't be doing accounting for
+        * that type anymore.
+        */
+       if (!XFS_IS_UQUOTA_ON(mp))
+               mp->m_qflags &= ~XFS_UQUOTA_CHKD;
+       if (!XFS_IS_GQUOTA_ON(mp))
+               mp->m_qflags &= ~XFS_GQUOTA_CHKD;
+       if (!XFS_IS_PQUOTA_ON(mp))
+               mp->m_qflags &= ~XFS_PQUOTA_CHKD;
+
+ write_changes:
+       /*
+        * We actually don't have to acquire the m_sb_lock at all.
+        * This can only be called from mount, and that's single threaded. XXX
+        */
+       spin_lock(&mp->m_sb_lock);
+       sbf = mp->m_sb.sb_qflags;
+       mp->m_sb.sb_qflags = mp->m_qflags & XFS_MOUNT_QUOTA_ALL;
+       spin_unlock(&mp->m_sb_lock);
+
+       if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) {
+               if (xfs_sync_sb(mp, false)) {
+                       /*
+                        * We could only have been turning quotas off.
+                        * We aren't in very good shape actually because
+                        * the incore structures are convinced that quotas are
+                        * off, but the on disk superblock doesn't know that !
+                        */
+                       ASSERT(!(XFS_IS_QUOTA_RUNNING(mp)));
+                       xfs_alert(mp, "%s: Superblock update failed!",
+                               __func__);
+               }
+       }
+
+       if (error) {
+               xfs_warn(mp, "Failed to initialize disk quotas.");
+               return;
+       }
 }
 
 /*
@@ -1395,15 +1481,13 @@ STATIC int
 xfs_qm_init_quotainos(
        xfs_mount_t     *mp)
 {
-       xfs_inode_t     *uip, *gip;
-       int             error;
-       __int64_t       sbflags;
-       uint            flags;
+       struct xfs_inode        *uip = NULL;
+       struct xfs_inode        *gip = NULL;
+       struct xfs_inode        *pip = NULL;
+       int                     error;
+       uint                    flags = 0;
 
        ASSERT(mp->m_quotainfo);
-       uip = gip = NULL;
-       sbflags = 0;
-       flags = 0;
 
        /*
         * Get the uquota and gquota inodes
@@ -1412,57 +1496,74 @@ 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)))
-                               return XFS_ERROR(error);
+                       error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
+                                            0, 0, &uip);
+                       if (error)
+                               return 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;
+               }
+               if (XFS_IS_PQUOTA_ON(mp) &&
+                   mp->m_sb.sb_pquotino != NULLFSINO) {
+                       ASSERT(mp->m_sb.sb_pquotino > 0);
+                       error = xfs_iget(mp, NULL, mp->m_sb.sb_pquotino,
+                                            0, 0, &pip);
+                       if (error)
+                               goto error_rele;
                }
        } else {
                flags |= XFS_QMOPT_SBVERSION;
-               sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
-                           XFS_SB_GQUOTINO | XFS_SB_QFLAGS);
        }
 
        /*
-        * 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,
-                                             sbflags | XFS_SB_UQUOTINO,
-                                             flags | XFS_QMOPT_UQUOTA)))
-                       return XFS_ERROR(error);
+               error = xfs_qm_qino_alloc(mp, &uip,
+                                             flags | XFS_QMOPT_UQUOTA);
+               if (error)
+                       goto error_rele;
 
                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);
+                                         flags | XFS_QMOPT_GQUOTA);
+               if (error)
+                       goto error_rele;
 
-                       return XFS_ERROR(error);
-               }
+               flags &= ~XFS_QMOPT_SBVERSION;
+       }
+       if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) {
+               error = xfs_qm_qino_alloc(mp, &pip,
+                                         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 error;
 }
 
 STATIC void
@@ -1473,7 +1574,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--;
@@ -1482,159 +1583,6 @@ xfs_qm_dqfree_one(
        xfs_qm_dqdestroy(dqp);
 }
 
-STATIC void
-xfs_qm_dqreclaim_one(
-       struct xfs_dquot        *dqp,
-       struct list_head        *buffer_list,
-       struct list_head        *dispose_list)
-{
-       struct xfs_mount        *mp = dqp->q_mount;
-       struct xfs_quotainfo    *qi = mp->m_quotainfo;
-       int                     error;
-
-       if (!xfs_dqlock_nowait(dqp))
-               goto out_move_tail;
-
-       /*
-        * This dquot has acquired a reference in the meantime remove it from
-        * the freelist and try again.
-        */
-       if (dqp->q_nrefs) {
-               xfs_dqunlock(dqp);
-
-               trace_xfs_dqreclaim_want(dqp);
-               XFS_STATS_INC(xs_qm_dqwants);
-
-               list_del_init(&dqp->q_lru);
-               qi->qi_lru_count--;
-               XFS_STATS_DEC(xs_qm_dquot_unused);
-               return;
-       }
-
-       /*
-        * Try to grab the flush lock. If this dquot is in the process of
-        * getting flushed to disk, we don't want to reclaim it.
-        */
-       if (!xfs_dqflock_nowait(dqp))
-               goto out_unlock_move_tail;
-
-       if (XFS_DQ_IS_DIRTY(dqp)) {
-               struct xfs_buf  *bp = NULL;
-
-               trace_xfs_dqreclaim_dirty(dqp);
-
-               error = xfs_qm_dqflush(dqp, &bp);
-               if (error) {
-                       xfs_warn(mp, "%s: dquot %p flush failed",
-                                __func__, dqp);
-                       goto out_unlock_move_tail;
-               }
-
-               xfs_buf_delwri_queue(bp, buffer_list);
-               xfs_buf_relse(bp);
-               /*
-                * Give the dquot another try on the freelist, as the
-                * flushing will take some time.
-                */
-               goto out_unlock_move_tail;
-       }
-       xfs_dqfunlock(dqp);
-
-       /*
-        * Prevent lookups now that we are past the point of no return.
-        */
-       dqp->dq_flags |= XFS_DQ_FREEING;
-       xfs_dqunlock(dqp);
-
-       ASSERT(dqp->q_nrefs == 0);
-       list_move_tail(&dqp->q_lru, dispose_list);
-       qi->qi_lru_count--;
-       XFS_STATS_DEC(xs_qm_dquot_unused);
-
-       trace_xfs_dqreclaim_done(dqp);
-       XFS_STATS_INC(xs_qm_dqreclaims);
-       return;
-
-       /*
-        * Move the dquot to the tail of the list so that we don't spin on it.
-        */
-out_unlock_move_tail:
-       xfs_dqunlock(dqp);
-out_move_tail:
-       list_move_tail(&dqp->q_lru, &qi->qi_lru_list);
-       trace_xfs_dqreclaim_busy(dqp);
-       XFS_STATS_INC(xs_qm_dqreclaim_misses);
-}
-
-STATIC int
-xfs_qm_shake(
-       struct shrinker         *shrink,
-       struct shrink_control   *sc)
-{
-       struct xfs_quotainfo    *qi =
-               container_of(shrink, struct xfs_quotainfo, qi_shrinker);
-       int                     nr_to_scan = sc->nr_to_scan;
-       LIST_HEAD               (buffer_list);
-       LIST_HEAD               (dispose_list);
-       struct xfs_dquot        *dqp;
-       int                     error;
-
-       if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT))
-               return 0;
-       if (!nr_to_scan)
-               goto out;
-
-       mutex_lock(&qi->qi_lru_lock);
-       while (!list_empty(&qi->qi_lru_list)) {
-               if (nr_to_scan-- <= 0)
-                       break;
-               dqp = list_first_entry(&qi->qi_lru_list, struct xfs_dquot,
-                                      q_lru);
-               xfs_qm_dqreclaim_one(dqp, &buffer_list, &dispose_list);
-       }
-       mutex_unlock(&qi->qi_lru_lock);
-
-       error = xfs_buf_delwri_submit(&buffer_list);
-       if (error)
-               xfs_warn(NULL, "%s: dquot reclaim failed", __func__);
-
-       while (!list_empty(&dispose_list)) {
-               dqp = list_first_entry(&dispose_list, struct xfs_dquot, q_lru);
-               list_del_init(&dqp->q_lru);
-               xfs_qm_dqfree_one(dqp);
-       }
-
-out:
-       return (qi->qi_lru_count / 100) * sysctl_vfs_cache_pressure;
-}
-
-/*
- * Start a transaction and write the incore superblock changes to
- * disk. flags parameter indicates which fields have changed.
- */
-int
-xfs_qm_write_sb_changes(
-       xfs_mount_t     *mp,
-       __int64_t       flags)
-{
-       xfs_trans_t     *tp;
-       int             error;
-
-       tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE);
-       error = xfs_trans_reserve(tp, 0, XFS_QM_SBCHANGE_LOG_RES(mp),
-                                 0, 0, XFS_DEFAULT_LOG_COUNT);
-       if (error) {
-               xfs_trans_cancel(tp, 0);
-               return error;
-       }
-
-       xfs_mod_sb(tp, flags);
-       error = xfs_trans_commit(tp, 0);
-
-       return error;
-}
-
-
 /* --------------- utility functions for vnodeops ---------------- */
 
 
@@ -1651,15 +1599,18 @@ xfs_qm_write_sb_changes(
 int
 xfs_qm_vop_dqalloc(
        struct xfs_inode        *ip,
-       uid_t                   uid,
-       gid_t                   gid,
+       xfs_dqid_t              uid,
+       xfs_dqid_t              gid,
        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;
 
@@ -1684,7 +1635,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) {
                        /*
@@ -1697,12 +1647,13 @@ 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, uid,
                                                 XFS_DQ_USER,
                                                 XFS_QMOPT_DQALLOC |
                                                 XFS_QMOPT_DOWARN,
-                                                &uq))) {
-                               ASSERT(error != ENOENT);
+                                                &uq);
+                       if (error) {
+                               ASSERT(error != -ENOENT);
                                return error;
                        }
                        /*
@@ -1723,15 +1674,14 @@ 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, gid,
                                                 XFS_DQ_GROUP,
                                                 XFS_QMOPT_DQALLOC |
                                                 XFS_QMOPT_DOWARN,
-                                                &gq))) {
-                               if (uq)
-                                       xfs_qm_dqrele(uq);
-                               ASSERT(error != ENOENT);
-                               return error;
+                                                &gq);
+                       if (error) {
+                               ASSERT(error != -ENOENT);
+                               goto error_rele;
                        }
                        xfs_dqunlock(gq);
                        lockflags = XFS_ILOCK_SHARED;
@@ -1740,25 +1690,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);
+                                                &pq);
+                       if (error) {
+                               ASSERT(error != -ENOENT);
+                               goto error_rele;
                        }
-                       xfs_dqunlock(gq);
+                       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)
@@ -1767,13 +1717,22 @@ xfs_qm_vop_dqalloc(
        xfs_iunlock(ip, lockflags);
        if (O_udqpp)
                *O_udqpp = uq;
-       else if (uq)
+       else
                xfs_qm_dqrele(uq);
        if (O_gdqpp)
                *O_gdqpp = gq;
-       else if (gq)
+       else
                xfs_qm_dqrele(gq);
+       if (O_pdqpp)
+               *O_pdqpp = pq;
+       else
+               xfs_qm_dqrele(pq);
        return 0;
+
+error_rele:
+       xfs_qm_dqrele(gq);
+       xfs_qm_dqrele(uq);
+       return error;
 }
 
 /*
@@ -1821,29 +1780,34 @@ 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)
+       struct xfs_trans        *tp,
+       struct xfs_inode        *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;
-       int             error;
+       struct xfs_mount        *mp = ip->i_mount;
+       uint                    delblks, blkflags, prjflags = 0;
+       struct xfs_dquot        *udq_unres = NULL;
+       struct xfs_dquot        *gdq_unres = NULL;
+       struct xfs_dquot        *pdq_unres = NULL;
+       struct xfs_dquot        *udq_delblks = NULL;
+       struct xfs_dquot        *gdq_delblks = NULL;
+       struct xfs_dquot        *pdq_delblks = NULL;
+       int                     error;
 
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
        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;
 
        if (XFS_IS_UQUOTA_ON(mp) && udqp &&
-           ip->i_d.di_uid != (uid_t)be32_to_cpu(udqp->q_core.d_id)) {
-               delblksudq = udqp;
+           ip->i_d.di_uid != be32_to_cpu(udqp->q_core.d_id)) {
+               udq_delblks = udqp;
                /*
                 * If there are delayed allocation blocks, then we have to
                 * unreserve those from the old dquot, and add them to the
@@ -1851,29 +1815,34 @@ xfs_qm_vop_chown_reserve(
                 */
                if (delblks) {
                        ASSERT(ip->i_udquot);
-                       unresudq = ip->i_udquot;
+                       udq_unres = 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)) {
+               gdq_delblks = gdqp;
+               if (delblks) {
+                       ASSERT(ip->i_gdquot);
+                       gdq_unres = ip->i_gdquot;
                }
        }
 
-       if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
-                               delblksudq, delblksgdq, ip->i_d.di_nblocks, 1,
-                               flags | blkflags | prjflags)))
-               return (error);
+       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;
+               pdq_delblks = pdqp;
+               if (delblks) {
+                       ASSERT(ip->i_pdquot);
+                       pdq_unres = ip->i_pdquot;
+               }
+       }
+
+       error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
+                               udq_delblks, gdq_delblks, pdq_delblks,
+                               ip->i_d.di_nblocks, 1,
+                               flags | blkflags | prjflags);
+       if (error)
+               return error;
 
        /*
         * Do the delayed blks reservations/unreservations now. Since, these
@@ -1885,18 +1854,20 @@ 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)))
-                       return (error);
+               ASSERT(udq_delblks || gdq_delblks || pdq_delblks);
+               ASSERT(udq_unres || gdq_unres || pdq_unres);
+               error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
+                           udq_delblks, gdq_delblks, pdq_delblks,
+                           (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);
+                               udq_unres, gdq_unres, pdq_unres,
+                               -((xfs_qcnt_t)delblks), 0, blkflags);
        }
 
-       return (0);
+       return 0;
 }
 
 int
@@ -1932,7 +1903,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;
 
@@ -1942,23 +1914,25 @@ xfs_qm_vop_create_dqattach(
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
        ASSERT(XFS_IS_QUOTA_RUNNING(mp));
 
-       if (udqp) {
+       if (udqp && XFS_IS_UQUOTA_ON(mp)) {
                ASSERT(ip->i_udquot == NULL);
-               ASSERT(XFS_IS_UQUOTA_ON(mp));
                ASSERT(ip->i_d.di_uid == be32_to_cpu(udqp->q_core.d_id));
 
                ip->i_udquot = xfs_qm_dqhold(udqp);
                xfs_trans_mod_dquot(tp, udqp, XFS_TRANS_DQ_ICOUNT, 1);
        }
-       if (gdqp) {
+       if (gdqp && XFS_IS_GQUOTA_ON(mp)) {
                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(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 && XFS_IS_PQUOTA_ON(mp)) {
+               ASSERT(ip->i_pdquot == NULL);
+               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);
+       }
 }