📄 xfs_qm.c
字号:
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); 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)) 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, prevdq, XFS_TRANS_DQ_ICOUNT, -1); /* the sparkling new dquot */ xfs_trans_mod_dquot(tp, newdq, bfield, ip->i_d.di_nblocks); xfs_trans_mod_dquot(tp, newdq, XFS_TRANS_DQ_ICOUNT, 1); /* * Take an extra reference, because the inode * is going to keep this dquot pointer even * after the trans_commit. */ xfs_dqlock(newdq); XFS_DQHOLD(newdq); xfs_dqunlock(newdq); *IO_olddq = newdq; return prevdq;}/* * Quota reservations for setattr(AT_UID|AT_GID|AT_PROJID). */intxfs_qm_vop_chown_reserve( xfs_trans_t *tp, xfs_inode_t *ip, xfs_dquot_t *udqp, xfs_dquot_t *gdqp, uint flags){ int error; xfs_mount_t *mp; uint delblks, blkflags, prjflags = 0; xfs_dquot_t *unresudq, *unresgdq, *delblksudq, *delblksgdq; ASSERT(XFS_ISLOCKED_INODE(ip)); mp = ip->i_mount; 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; /* * If there are delayed allocation blocks, then we have to * unreserve those from the old dquot, and add them to the * new dquot. */ if (delblks) { ASSE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -