STATIC int xfs_dqrele_inode( struct xfs_inode *ip, struct xfs_perag *pag, int flags) { int error; /* skip quota inodes */ if (ip == XFS_QI_UQIP(ip->i_mount) || ip == XFS_QI_GQIP(ip->i_mount)) { ASSERT(ip->i_udquot == NULL); ASSERT(ip->i_gdquot == NULL); read_unlock(&pag->pag_ici_lock); return 0; } error = xfs_sync_inode_valid(ip, pag); if (error) return error; xfs_ilock(ip, XFS_ILOCK_EXCL); if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) { xfs_qm_dqrele(ip->i_udquot); ip->i_udquot = NULL; } if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) { xfs_qm_dqrele(ip->i_gdquot); ip->i_gdquot = NULL; } xfs_iput(ip, XFS_ILOCK_EXCL); return 0; }
/* * Turn off quota accounting and/or enforcement for all udquots and/or * gdquots. Called only at unmount time. * * This assumes that there are no dquots of this file system cached * incore, and modifies the ondisk dquot directly. Therefore, for example, * it is an error to call this twice, without purging the cache. */ STATIC int xfs_qm_scall_quotaoff( xfs_mount_t *mp, uint flags, boolean_t force) { uint dqtype; unsigned long s; int error; uint inactivate_flags; xfs_qoff_logitem_t *qoffstart; int nculprits; if (!force && !capable(CAP_SYS_ADMIN)) return XFS_ERROR(EPERM); /* * No file system can have quotas enabled on disk but not in core. * Note that quota utilities (like quotaoff) _expect_ * errno == EEXIST here. */ if ((mp->m_qflags & flags) == 0) return XFS_ERROR(EEXIST); error = 0; flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD); /* * We don't want to deal with two quotaoffs messing up each other, * so we're going to serialize it. quotaoff isn't exactly a performance * critical thing. * If quotaoff, then we must be dealing with the root filesystem. */ ASSERT(mp->m_quotainfo); if (mp->m_quotainfo) mutex_lock(&(XFS_QI_QOFFLOCK(mp))); ASSERT(mp->m_quotainfo); /* * If we're just turning off quota enforcement, change mp and go. */ if ((flags & XFS_ALL_QUOTA_ACCT) == 0) { mp->m_qflags &= ~(flags); s = XFS_SB_LOCK(mp); mp->m_sb.sb_qflags = mp->m_qflags; XFS_SB_UNLOCK(mp, s); mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); /* XXX what to do if error ? Revert back to old vals incore ? */ error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS); return (error); } dqtype = 0; inactivate_flags = 0; /* * If accounting is off, we must turn enforcement off, clear the * quota 'CHKD' certificate to make it known that we have to * do a quotacheck the next time this quota is turned on. */ if (flags & XFS_UQUOTA_ACCT) { dqtype |= XFS_QMOPT_UQUOTA; flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD); inactivate_flags |= XFS_UQUOTA_ACTIVE; } if (flags & XFS_GQUOTA_ACCT) { dqtype |= XFS_QMOPT_GQUOTA; flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD); inactivate_flags |= XFS_GQUOTA_ACTIVE; } else if (flags & XFS_PQUOTA_ACCT) { dqtype |= XFS_QMOPT_PQUOTA; flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD); inactivate_flags |= XFS_PQUOTA_ACTIVE; } /* * Nothing to do? Don't complain. This happens when we're just * turning off quota enforcement. */ if ((mp->m_qflags & flags) == 0) { mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); return (0); } /* * Write the LI_QUOTAOFF log record, and do SB changes atomically, * and synchronously. */ xfs_qm_log_quotaoff(mp, &qoffstart, flags); /* * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct * to take care of the race between dqget and quotaoff. We don't take * any special locks to reset these bits. All processes need to check * these bits *after* taking inode lock(s) to see if the particular * quota type is in the process of being turned off. If *ACTIVE, it is * guaranteed that all dquot structures and all quotainode ptrs will all * stay valid as long as that inode is kept locked. * * There is no turning back after this. */ mp->m_qflags &= ~inactivate_flags; /* * Give back all the dquot reference(s) held by inodes. * Here we go thru every single incore inode in this file system, and * do a dqrele on the i_udquot/i_gdquot that it may have. * Essentially, as long as somebody has an inode locked, this guarantees * that quotas will not be turned off. This is handy because in a * transaction once we lock the inode(s) and check for quotaon, we can * depend on the quota inodes (and other things) being valid as long as * we keep the lock(s). */ xfs_qm_dqrele_all_inodes(mp, flags); /* * Next we make the changes in the quota flag in the mount struct. * This isn't protected by a particular lock directly, because we * don't want to take a mrlock everytime we depend on quotas being on. */ mp->m_qflags &= ~(flags); /* * Go through all the dquots of this file system and purge them, * according to what was turned off. We may not be able to get rid * of all dquots, because dquots can have temporary references that * are not attached to inodes. eg. xfs_setattr, xfs_create. * So, if we couldn't purge all the dquots from the filesystem, * we can't get rid of the incore data structures. */ while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype|XFS_QMOPT_QUOTAOFF))) delay(10 * nculprits); /* * Transactions that had started before ACTIVE state bit was cleared * could have logged many dquots, so they'd have higher LSNs than * the first QUOTAOFF log record does. If we happen to crash when * the tail of the log has gone past the QUOTAOFF record, but * before the last dquot modification, those dquots __will__ * recover, and that's not good. * * So, we have QUOTAOFF start and end logitems; the start * logitem won't get overwritten until the end logitem appears... */ xfs_qm_log_quotaoff_end(mp, qoffstart, flags); /* * If quotas is completely disabled, close shop. */ if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) || ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) { mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); xfs_qm_destroy_quotainfo(mp); return (0); } /* * Release our quotainode references, and vn_purge them, * if we don't need them anymore. */ if ((dqtype & XFS_QMOPT_UQUOTA) && XFS_QI_UQIP(mp)) { XFS_PURGE_INODE(XFS_QI_UQIP(mp)); XFS_QI_UQIP(mp) = NULL; } if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && XFS_QI_GQIP(mp)) { XFS_PURGE_INODE(XFS_QI_GQIP(mp)); XFS_QI_GQIP(mp) = NULL; } mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); return (error); }
/* * Go thru all the inodes in the file system, releasing their dquots. * Note that the mount structure gets modified to indicate that quotas are off * AFTER this, in the case of quotaoff. This also gets called from * xfs_rootumount. */ void xfs_qm_dqrele_all_inodes( struct xfs_mount *mp, uint flags) { xfs_inode_t *ip, *topino; uint ireclaims; bhv_vnode_t *vp; boolean_t vnode_refd; ASSERT(mp->m_quotainfo); XFS_MOUNT_ILOCK(mp); again: ip = mp->m_inodes; if (ip == NULL) { XFS_MOUNT_IUNLOCK(mp); return; } do { /* Skip markers inserted by xfs_sync */ if (ip->i_mount == NULL) { ip = ip->i_mnext; continue; } /* Root inode, rbmip and rsumip have associated blocks */ if (ip == XFS_QI_UQIP(mp) || ip == XFS_QI_GQIP(mp)) { ASSERT(ip->i_udquot == NULL); ASSERT(ip->i_gdquot == NULL); ip = ip->i_mnext; continue; } vp = XFS_ITOV_NULL(ip); if (!vp) { ASSERT(ip->i_udquot == NULL); ASSERT(ip->i_gdquot == NULL); ip = ip->i_mnext; continue; } vnode_refd = B_FALSE; if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) { ireclaims = mp->m_ireclaims; topino = mp->m_inodes; vp = vn_grab(vp); if (!vp) goto again; XFS_MOUNT_IUNLOCK(mp); /* XXX restart limit ? */ xfs_ilock(ip, XFS_ILOCK_EXCL); vnode_refd = B_TRUE; } else { ireclaims = mp->m_ireclaims; topino = mp->m_inodes; XFS_MOUNT_IUNLOCK(mp); } /* * We don't keep the mountlock across the dqrele() call, * since it can take a while.. */ if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) { xfs_qm_dqrele(ip->i_udquot); ip->i_udquot = NULL; } if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) { xfs_qm_dqrele(ip->i_gdquot); ip->i_gdquot = NULL; } xfs_iunlock(ip, XFS_ILOCK_EXCL); /* * Wait until we've dropped the ilock and mountlock to * do the vn_rele. Or be condemned to an eternity in the * inactive code in hell. */ if (vnode_refd) VN_RELE(vp); XFS_MOUNT_ILOCK(mp); /* * If an inode was inserted or removed, we gotta * start over again. */ if (topino != mp->m_inodes || mp->m_ireclaims != ireclaims) { /* XXX use a sentinel */ goto again; } ip = ip->i_mnext; } while (ip != mp->m_inodes); XFS_MOUNT_IUNLOCK(mp); }