📄 xfs_qm.c
字号:
*/ xfs_qm_dqflock_pushbuf_wait(dqp); } /* * Let go of the mplist lock. We don't want to hold it * across a disk write. */ xfs_qm_mplist_unlock(mp); error = xfs_qm_dqflush(dqp, flags); xfs_dqunlock(dqp); if (error) return (error); xfs_qm_mplist_lock(mp); if (recl != XFS_QI_MPLRECLAIMS(mp)) { xfs_qm_mplist_unlock(mp); /* XXX restart limit */ goto again; } } xfs_qm_mplist_unlock(mp); /* return ! busy */ return (0);}/* * Release the group dquot pointers the user dquots may be * carrying around as a hint. mplist is locked on entry and exit. */STATIC voidxfs_qm_detach_gdquots( xfs_mount_t *mp){ xfs_dquot_t *dqp, *gdqp; int nrecl; again: ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); dqp = XFS_QI_MPLNEXT(mp); while (dqp) { xfs_dqlock(dqp); if ((gdqp = dqp->q_gdquot)) { xfs_dqlock(gdqp); dqp->q_gdquot = NULL; } xfs_dqunlock(dqp); if (gdqp) { /* * Can't hold the mplist lock across a dqput. * XXXmust convert to marker based iterations here. */ nrecl = XFS_QI_MPLRECLAIMS(mp); xfs_qm_mplist_unlock(mp); xfs_qm_dqput(gdqp); xfs_qm_mplist_lock(mp); if (nrecl != XFS_QI_MPLRECLAIMS(mp)) goto again; } dqp = dqp->MPL_NEXT; }}/* * Go through all the incore dquots of this file system and take them * off the mplist and hashlist, if the dquot type matches the dqtype * parameter. This is used when turning off quota accounting for * users and/or groups, as well as when the filesystem is unmounting. */STATIC intxfs_qm_dqpurge_int( xfs_mount_t *mp, uint flags) /* QUOTAOFF/UMOUNTING/UQUOTA/PQUOTA/GQUOTA */{ xfs_dquot_t *dqp; uint dqtype; int nrecl; xfs_dquot_t *nextdqp; int nmisses; if (mp->m_quotainfo == NULL) return (0); dqtype = (flags & XFS_QMOPT_UQUOTA) ? XFS_DQ_USER : 0; dqtype |= (flags & XFS_QMOPT_PQUOTA) ? XFS_DQ_PROJ : 0; dqtype |= (flags & XFS_QMOPT_GQUOTA) ? XFS_DQ_GROUP : 0; xfs_qm_mplist_lock(mp); /* * In the first pass through all incore dquots of this filesystem, * we release the group dquot pointers the user dquots may be * carrying around as a hint. We need to do this irrespective of * what's being turned off. */ xfs_qm_detach_gdquots(mp); again: nmisses = 0; ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); /* * Try to get rid of all of the unwanted dquots. The idea is to * get them off mplist and hashlist, but leave them on freelist. */ dqp = XFS_QI_MPLNEXT(mp); while (dqp) { /* * It's OK to look at the type without taking dqlock here. * We're holding the mplist lock here, and that's needed for * a dqreclaim. */ if ((dqp->dq_flags & dqtype) == 0) { dqp = dqp->MPL_NEXT; continue; } if (! xfs_qm_dqhashlock_nowait(dqp)) { nrecl = XFS_QI_MPLRECLAIMS(mp); xfs_qm_mplist_unlock(mp); XFS_DQ_HASH_LOCK(dqp->q_hash); xfs_qm_mplist_lock(mp); /* * XXXTheoretically, we can get into a very long * ping pong game here. * No one can be adding dquots to the mplist at * this point, but somebody might be taking things off. */ if (nrecl != XFS_QI_MPLRECLAIMS(mp)) { XFS_DQ_HASH_UNLOCK(dqp->q_hash); goto again; } } /* * Take the dquot off the mplist and hashlist. It may remain on * freelist in INACTIVE state. */ nextdqp = dqp->MPL_NEXT; nmisses += xfs_qm_dqpurge(dqp, flags); dqp = nextdqp; } xfs_qm_mplist_unlock(mp); return nmisses;}intxfs_qm_dqpurge_all( xfs_mount_t *mp, uint flags){ int ndquots; /* * Purge the dquot cache. * None of the dquots should really be busy at this point. */ if (mp->m_quotainfo) { while ((ndquots = xfs_qm_dqpurge_int(mp, flags))) { delay(ndquots * 10); } } return 0;}STATIC intxfs_qm_dqattach_one( xfs_inode_t *ip, xfs_dqid_t id, uint type, uint doalloc, uint dolock, xfs_dquot_t *udqhint, /* hint */ xfs_dquot_t **IO_idqpp){ xfs_dquot_t *dqp; int error; ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); 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. */ if ((dqp = *IO_idqpp)) { if (dolock) xfs_dqlock(dqp); xfs_dqtrace_entry(dqp, "DQATTACH: found in ip"); goto done; } /* * 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. */ ASSERT(!udqhint || type == XFS_DQ_GROUP || type == XFS_DQ_PROJ); if (udqhint && !dolock) 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. */ if (udqhint && (dqp = udqhint->q_gdquot) && (be32_to_cpu(dqp->q_core.d_id) == id)) { ASSERT(XFS_DQ_IS_LOCKED(udqhint)); xfs_dqlock(dqp); XFS_DQHOLD(dqp); ASSERT(*IO_idqpp == NULL); *IO_idqpp = dqp; if (!dolock) { xfs_dqunlock(dqp); xfs_dqunlock(udqhint); } goto done; } /* * 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(). */ if (udqhint) 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. */ if ((error = xfs_qm_dqget(ip->i_mount, ip, id, type, doalloc|XFS_QMOPT_DOWARN, &dqp))) { if (udqhint && dolock) xfs_dqlock(udqhint); goto done; } xfs_dqtrace_entry(dqp, "DQATTACH: found by dqget"); /* * dqget may have dropped and re-acquired the ilock, but it guarantees * that the dquot returned is the one that should go in the inode. */ *IO_idqpp = dqp; ASSERT(dqp); ASSERT(XFS_DQ_IS_LOCKED(dqp)); if (! dolock) { xfs_dqunlock(dqp); goto done; } if (! udqhint) goto done; ASSERT(udqhint); ASSERT(dolock); ASSERT(XFS_DQ_IS_LOCKED(dqp)); if (! xfs_qm_dqlock_nowait(udqhint)) { xfs_dqunlock(dqp); xfs_dqlock(udqhint); xfs_dqlock(dqp); } done:#ifdef QUOTADEBUG if (udqhint) { if (dolock) ASSERT(XFS_DQ_IS_LOCKED(udqhint)); } if (! error) { if (dolock) ASSERT(XFS_DQ_IS_LOCKED(dqp)); }#endif return (error);}/* * Given a udquot and gdquot, attach a ptr to the group dquot in the * udquot as a hint for future lookups. The idea sounds simple, but the * execution isn't, because the udquot might have a group dquot attached * already and getting rid of that gets us into lock ordering contraints. * The process is complicated more by the fact that the dquots may or may not * be locked on entry. */STATIC voidxfs_qm_dqattach_grouphint( xfs_dquot_t *udq, xfs_dquot_t *gdq, uint locked){ xfs_dquot_t *tmp;#ifdef QUOTADEBUG if (locked) { ASSERT(XFS_DQ_IS_LOCKED(udq)); ASSERT(XFS_DQ_IS_LOCKED(gdq)); }#endif if (! locked) xfs_dqlock(udq); if ((tmp = udq->q_gdquot)) { if (tmp == gdq) { if (! locked) xfs_dqunlock(udq); return; } udq->q_gdquot = NULL; /* * We can't keep any dqlocks when calling dqrele, * because the freelist lock comes before dqlocks. */ xfs_dqunlock(udq); if (locked) xfs_dqunlock(gdq); /* * we took a hard reference once upon a time in dqget, * so give it back when the udquot no longer points at it * dqput() does the unlocking of the dquot. */ xfs_qm_dqrele(tmp); xfs_dqlock(udq); xfs_dqlock(gdq); } else { ASSERT(XFS_DQ_IS_LOCKED(udq)); if (! locked) { xfs_dqlock(gdq); } } ASSERT(XFS_DQ_IS_LOCKED(udq)); ASSERT(XFS_DQ_IS_LOCKED(gdq)); /* * Somebody could have attached a gdquot here, * when we dropped the uqlock. If so, just do nothing. */ if (udq->q_gdquot == NULL) { XFS_DQHOLD(gdq); udq->q_gdquot = gdq; } if (! locked) { xfs_dqunlock(gdq); xfs_dqunlock(udq); }}/* * Given a locked inode, attach dquot(s) to it, taking U/G/P-QUOTAON * into account. * If XFS_QMOPT_DQALLOC, the dquot(s) will be allocated if needed. * If XFS_QMOPT_DQLOCK, the dquot(s) will be returned locked. This option pretty * much made this code a complete mess, but it has been pretty useful. * If XFS_QMOPT_ILOCKED, then inode sent is already locked EXCL. * Inode may get unlocked and relocked in here, and the caller must deal with * the consequences. */intxfs_qm_dqattach( xfs_inode_t *ip, uint flags){ xfs_mount_t *mp = ip->i_mount; uint nquotas = 0; int error = 0; if ((! XFS_IS_QUOTA_ON(mp)) || (! XFS_NOT_DQATTACHED(mp, ip)) || (ip->i_ino == mp->m_sb.sb_uquotino) || (ip->i_ino == mp->m_sb.sb_gquotino)) return (0); ASSERT((flags & XFS_QMOPT_ILOCKED) == 0 || XFS_ISLOCKED_INODE_EXCL(ip)); if (! (flags & XFS_QMOPT_ILOCKED)) xfs_ilock(ip, XFS_ILOCK_EXCL); if (XFS_IS_UQUOTA_ON(mp)) { error = xfs_qm_dqattach_one(ip, ip->i_d.di_uid, XFS_DQ_USER, flags & XFS_QMOPT_DQALLOC, flags & XFS_QMOPT_DQLOCK, NULL, &ip->i_udquot); if (error) goto done; nquotas++; } ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); 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, flags & XFS_QMOPT_DQLOCK, ip->i_udquot, &ip->i_gdquot) : xfs_qm_dqattach_one(ip, ip->i_d.di_projid, XFS_DQ_PROJ, flags & XFS_QMOPT_DQALLOC, flags & XFS_QMOPT_DQLOCK, 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. */ if (error) goto done; nquotas++; } /* * Attach this group quota to the user quota as a hint. * This WON'T, in general, result in a thrash. */ if (nquotas == 2) { ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); ASSERT(ip->i_udquot); ASSERT(ip->i_gdquot); /* * We may or may not have the 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) goto done; /* * Attach i_gdquot to the gdquot hint inside the i_udquot. */ xfs_qm_dqattach_grouphint(ip->i_udquot, ip->i_gdquot, flags & XFS_QMOPT_DQLOCK); } done:#ifdef QUOTADEBUG if (! error) { if (ip->i_udquot) { if (flags & XFS_QMOPT_DQLOCK) ASSERT(XFS_DQ_IS_LOCKED(ip->i_udquot)); } if (ip->i_gdquot) { if (flags & XFS_QMOPT_DQLOCK) ASSERT(XFS_DQ_IS_LOCKED(ip->i_gdquot)); } if (XFS_IS_UQUOTA_ON(mp)) ASSERT(ip->i_udquot); if (XFS_IS_OQUOTA_ON(mp)) ASSERT(ip->i_gdquot); }#endif if (! (flags & XFS_QMOPT_ILOCKED)) xfs_iunlock(ip, XFS_ILOCK_EXCL);#ifdef QUOTADEBUG else ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));#endif return (error);}/* * Release dquots (and their references) if any. * The inode should be locked EXCL except when this's called by * xfs_ireclaim. */voidxfs_qm_dqdetach( xfs_inode_t *ip){ if (!(ip->i_udquot || ip->i_gdquot)) return; ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_uquotino); ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_gquotino); if (ip->i_udquot) { xfs_dqtrace_entry_ino(ip->i_udquot, "DQDETTACH", ip); xfs_qm_dqrele(ip->i_udquot); ip->i_udquot = NULL; } if (ip->i_gdquot) { xfs_dqtrace_entry_ino(ip->i_gdquot, "DQDETTACH", ip); xfs_qm_dqrele(ip->i_gdquot); ip->i_gdquot = NULL; }}/* * This is called by VFS_SYNC and flags arg determines the caller, * and its motives, as done in xfs_sync. * * vfs_sync: SYNC_FSDATA|SYNC_ATTR|SYNC_BDFLUSH 0x31 * syscall sync: SYNC_FSDATA|SYNC_ATTR|SYNC_DELWRI 0x25 * umountroot : SYNC_WAIT | SYNC_CLOSE | SYNC_ATTR | SYNC_FSDATA */intxfs_qm_sync( xfs_mount_t *mp, short flags){ int recl, restarts; xfs_dquot_t *dqp; uint flush_flags; boolean_t nowait; int error; restarts = 0; /* * We won't block unless we are asked to.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -