📄 xfs_qm.c
字号:
for (dqp = xfs_Gqm->qm_dqfreelist.qh_next; ((dqp != (xfs_dquot_t *) &xfs_Gqm->qm_dqfreelist) && nreclaimed < howmany); ) { xfs_dqlock(dqp); /* * We are racing with dqlookup here. Naturally we don't * want to reclaim a dquot that lookup wants. */ if (dqp->dq_flags & XFS_DQ_WANT) { xfs_dqunlock(dqp); xfs_qm_freelist_unlock(xfs_Gqm); if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) return (nreclaimed); XQM_STATS_INC(xqmstats.xs_qm_dqwants); goto tryagain; } /* * If the dquot is inactive, we are assured that it is * not on the mplist or the hashlist, and that makes our * life easier. */ if (dqp->dq_flags & XFS_DQ_INACTIVE) { ASSERT(dqp->q_mount == NULL); ASSERT(! XFS_DQ_IS_DIRTY(dqp)); ASSERT(dqp->HL_PREVP == NULL); ASSERT(dqp->MPL_PREVP == NULL); XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims); nextdqp = dqp->dq_flnext; goto off_freelist; } ASSERT(dqp->MPL_PREVP); /* * 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_qm_dqflock_nowait(dqp)) { xfs_dqunlock(dqp); dqp = dqp->dq_flnext; continue; } /* * We have the flush lock so we know that this is not in the * process of being flushed. So, if this is dirty, flush it * DELWRI so that we don't get a freelist infested with * dirty dquots. */ if (XFS_DQ_IS_DIRTY(dqp)) { xfs_dqtrace_entry(dqp, "DQSHAKE: DQDIRTY"); /* * We flush it delayed write, so don't bother * releasing the mplock. */ (void) xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI); xfs_dqunlock(dqp); /* dqflush unlocks dqflock */ dqp = dqp->dq_flnext; continue; } /* * We're trying to get the hashlock out of order. This races * with dqlookup; so, we giveup and goto the next dquot if * we couldn't get the hashlock. This way, we won't starve * a dqlookup process that holds the hashlock that is * waiting for the freelist lock. */ if (! xfs_qm_dqhashlock_nowait(dqp)) { xfs_dqfunlock(dqp); xfs_dqunlock(dqp); dqp = dqp->dq_flnext; continue; } /* * This races with dquot allocation code as well as dqflush_all * and reclaim code. So, if we failed to grab the mplist lock, * giveup everything and start over. */ hash = dqp->q_hash; ASSERT(hash); if (! xfs_qm_mplist_nowait(dqp->q_mount)) { /* XXX put a sentinel so that we can come back here */ xfs_dqfunlock(dqp); xfs_dqunlock(dqp); XFS_DQ_HASH_UNLOCK(hash); xfs_qm_freelist_unlock(xfs_Gqm); if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) return (nreclaimed); goto tryagain; } xfs_dqtrace_entry(dqp, "DQSHAKE: UNLINKING");#ifdef QUOTADEBUG cmn_err(CE_DEBUG, "Shake 0x%p, ID 0x%x\n", dqp, be32_to_cpu(dqp->q_core.d_id));#endif ASSERT(dqp->q_nrefs == 0); nextdqp = dqp->dq_flnext; XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp); XQM_HASHLIST_REMOVE(hash, dqp); xfs_dqfunlock(dqp); xfs_qm_mplist_unlock(dqp->q_mount); XFS_DQ_HASH_UNLOCK(hash); off_freelist: XQM_FREELIST_REMOVE(dqp); xfs_dqunlock(dqp); nreclaimed++; XQM_STATS_INC(xqmstats.xs_qm_dqshake_reclaims); xfs_qm_dqdestroy(dqp); dqp = nextdqp; } xfs_qm_freelist_unlock(xfs_Gqm); return (nreclaimed);}/* * The kmem_shake interface is invoked when memory is running low. *//* ARGSUSED */STATIC intxfs_qm_shake(int nr_to_scan, gfp_t gfp_mask){ int ndqused, nfree, n; if (!kmem_shake_allow(gfp_mask)) return (0); if (!xfs_Gqm) return (0); nfree = xfs_Gqm->qm_dqfreelist.qh_nelems; /* free dquots */ /* incore dquots in all f/s's */ ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree; ASSERT(ndqused >= 0); if (nfree <= ndqused && nfree < ndquot) return (0); ndqused *= xfs_Gqm->qm_dqfree_ratio; /* target # of free dquots */ n = nfree - ndqused - ndquot; /* # over target */ return xfs_qm_shake_freelist(MAX(nfree, n));}/* * Just pop the least recently used dquot off the freelist and * recycle it. The returned dquot is locked. */STATIC xfs_dquot_t *xfs_qm_dqreclaim_one(void){ xfs_dquot_t *dqpout; xfs_dquot_t *dqp; int restarts; int nflushes; restarts = 0; dqpout = NULL; nflushes = 0; /* lockorder: hashchainlock, freelistlock, mplistlock, dqlock, dqflock */ startagain: xfs_qm_freelist_lock(xfs_Gqm); FOREACH_DQUOT_IN_FREELIST(dqp, &(xfs_Gqm->qm_dqfreelist)) { xfs_dqlock(dqp); /* * We are racing with dqlookup here. Naturally we don't * want to reclaim a dquot that lookup wants. We release the * freelist lock and start over, so that lookup will grab * both the dquot and the freelistlock. */ if (dqp->dq_flags & XFS_DQ_WANT) { ASSERT(! (dqp->dq_flags & XFS_DQ_INACTIVE)); xfs_dqtrace_entry(dqp, "DQRECLAIM: DQWANT"); xfs_dqunlock(dqp); xfs_qm_freelist_unlock(xfs_Gqm); if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) return (NULL); XQM_STATS_INC(xqmstats.xs_qm_dqwants); goto startagain; } /* * If the dquot is inactive, we are assured that it is * not on the mplist or the hashlist, and that makes our * life easier. */ if (dqp->dq_flags & XFS_DQ_INACTIVE) { ASSERT(dqp->q_mount == NULL); ASSERT(! XFS_DQ_IS_DIRTY(dqp)); ASSERT(dqp->HL_PREVP == NULL); ASSERT(dqp->MPL_PREVP == NULL); XQM_FREELIST_REMOVE(dqp); xfs_dqunlock(dqp); dqpout = dqp; XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims); break; } ASSERT(dqp->q_hash); ASSERT(dqp->MPL_PREVP); /* * 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_qm_dqflock_nowait(dqp)) { xfs_dqunlock(dqp); continue; } /* * We have the flush lock so we know that this is not in the * process of being flushed. So, if this is dirty, flush it * DELWRI so that we don't get a freelist infested with * dirty dquots. */ if (XFS_DQ_IS_DIRTY(dqp)) { xfs_dqtrace_entry(dqp, "DQRECLAIM: DQDIRTY"); /* * We flush it delayed write, so don't bother * releasing the freelist lock. */ (void) xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI); xfs_dqunlock(dqp); /* dqflush unlocks dqflock */ continue; } if (! xfs_qm_mplist_nowait(dqp->q_mount)) { xfs_dqfunlock(dqp); xfs_dqunlock(dqp); continue; } if (! xfs_qm_dqhashlock_nowait(dqp)) goto mplistunlock; ASSERT(dqp->q_nrefs == 0); xfs_dqtrace_entry(dqp, "DQRECLAIM: UNLINKING"); XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp); XQM_HASHLIST_REMOVE(dqp->q_hash, dqp); XQM_FREELIST_REMOVE(dqp); dqpout = dqp; XFS_DQ_HASH_UNLOCK(dqp->q_hash); mplistunlock: xfs_qm_mplist_unlock(dqp->q_mount); xfs_dqfunlock(dqp); xfs_dqunlock(dqp); if (dqpout) break; } xfs_qm_freelist_unlock(xfs_Gqm); return (dqpout);}/*------------------------------------------------------------------*//* * Return a new incore dquot. Depending on the number of * dquots in the system, we either allocate a new one on the kernel heap, * or reclaim a free one. * Return value is B_TRUE if we allocated a new dquot, B_FALSE if we managed * to reclaim an existing one from the freelist. */boolean_txfs_qm_dqalloc_incore( xfs_dquot_t **O_dqpp){ xfs_dquot_t *dqp; /* * Check against high water mark to see if we want to pop * a nincompoop dquot off the freelist. */ if (atomic_read(&xfs_Gqm->qm_totaldquots) >= ndquot) { /* * Try to recycle a dquot from the freelist. */ if ((dqp = xfs_qm_dqreclaim_one())) { XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); /* * Just zero the core here. The rest will get * reinitialized by caller. XXX we shouldn't even * do this zero ... */ memset(&dqp->q_core, 0, sizeof(dqp->q_core)); *O_dqpp = dqp; return (B_FALSE); } XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); } /* * Allocate a brand new dquot on the kernel heap and return it * to the caller to initialize. */ ASSERT(xfs_Gqm->qm_dqzone != NULL); *O_dqpp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); atomic_inc(&xfs_Gqm->qm_totaldquots); return (B_TRUE);}/* * Start a transaction and write the incore superblock changes to * disk. flags parameter indicates which fields have changed. */intxfs_qm_write_sb_changes( xfs_mount_t *mp, __int64_t flags){ xfs_trans_t *tp; int error;#ifdef QUOTADEBUG cmn_err(CE_NOTE, "Writing superblock quota changes :%s", mp->m_fsname);#endif tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE); if ((error = xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0, XFS_DEFAULT_LOG_COUNT))) { xfs_trans_cancel(tp, 0); return (error); } xfs_mod_sb(tp, flags); (void) xfs_trans_commit(tp, 0, NULL); return (0);}/* --------------- utility functions for vnodeops ---------------- *//* * Given an inode, a uid and gid (from cred_t) make sure that we have * allocated relevant dquot(s) on disk, and that we won't exceed inode * quotas by creating this file. * This also attaches dquot(s) to the given inode after locking it, * and returns the dquots corresponding to the uid and/or gid. * * in : inode (unlocked) * out : udquot, gdquot with references taken and unlocked */intxfs_qm_vop_dqalloc( xfs_mount_t *mp, xfs_inode_t *ip, uid_t uid, gid_t gid, prid_t prid, uint flags, xfs_dquot_t **O_udqpp, xfs_dquot_t **O_gdqpp){ int error; xfs_dquot_t *uq, *gq; uint lockflags; if (!XFS_IS_QUOTA_ON(mp)) return 0; lockflags = XFS_ILOCK_EXCL; xfs_ilock(ip, lockflags); if ((flags & XFS_QMOPT_INHERIT) && XFS_INHERIT_GID(ip, XFS_MTOVFS(mp))) gid = ip->i_d.di_gid; /* * Attach the dquot(s) to this inode, doing a dquot allocation * if necessary. The dquot(s) will not be locked. */ if (XFS_NOT_DQATTACHED(mp, ip)) { if ((error = xfs_qm_dqattach(ip, XFS_QMOPT_DQALLOC | XFS_QMOPT_ILOCKED))) { xfs_iunlock(ip, lockflags); return (error); } } uq = gq = NULL; if ((flags & XFS_QMOPT_UQUOTA) && XFS_IS_UQUOTA_ON(mp)) { if (ip->i_d.di_uid != uid) { /* * What we need is the dquot that has this uid, and * if we send the inode to dqget, the uid of the inode * takes priority over what's sent in the uid argument. * We must unlock inode here before calling dqget if * we're not sending the inode, because otherwise * we'll deadlock by doing trans_reserve while * holding ilock. */ xfs_iunlock(ip, lockflags); if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid, XFS_DQ_USER, XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, &uq))) { ASSERT(error != ENOENT); return (error); } /* * Get the ilock in the right order. */ xfs_dqunlock(uq); lockflags = XFS_ILOCK_SHARED; xfs_ilock(ip, lockflags); } else { /* * Take an extra reference, because we'll return * this to caller */ ASSERT(ip->i_udquot); uq = ip->i_udquot; xfs_dqlock(uq); XFS_DQHOLD(uq); xfs_dqunlock(uq); } } 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, XFS_DQ_GROUP, XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, &gq))) { if (uq) xfs_qm_dqrele(uq); ASSERT(error != ENOENT); return (error); } xfs_dqunlock(gq); lockflags = XFS_ILOCK_SHARED; xfs_ilock(ip, lockflags); } else { ASSERT(ip->i_gdquot); gq = ip->i_gdquot; xfs_dqlock(gq); XFS_DQHOLD(gq); xfs_dqunlock(gq); } } else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) { if (ip->i_d.di_projid != prid) { xfs_iunlock(ip, lockflags); if ((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); lockflags = XFS_ILOCK_SHARED; xfs_ilock(ip, lockflags); } else { ASSERT(ip->i_gdquot); gq = ip->i_gdquot; xfs_dqlock(gq); XFS_DQHOLD(gq); xfs_dqunlock(gq); } } if (uq) xfs_dqtrace_entry_ino(uq, "DQALLOC", ip); xfs_iunlock(ip, lockflags); if (O_udqpp) *O_udqpp = uq; else if (uq) xfs_qm_dqrele(uq); if (O_gdqpp) *O_gdqpp = gq; else if (gq) xfs_qm_dqrele(gq); return (0);}/* * Actually transfer ownership, and do dquot modifications. * These were already reserved. */xfs_dquot_t *xfs_qm_vop_chown( xfs_trans_t *tp, xfs_inode_t *ip, xfs_dquot_t **IO_olddq, xfs_dquot_t *newdq){ xfs_dquot_t *prevdq; uint bfield = XFS_IS_REALTIME_INODE(ip) ? XFS_TRANS_DQ_RTBCOUNT : XFS_TRANS_DQ_BCOUNT; ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); ASSERT(XFS_IS_QUOTA_RUNNING(ip->i_mount)); /* old dquot */ prevdq = *IO_olddq; ASSERT(prevdq); ASSERT(prevdq != newdq); xfs_trans_mod_dquot(tp, prevdq, bfield, -(ip->i_d.di_nblocks)); xfs_trans_mod_dquot(tp,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -