📄 xfs_qm.c
字号:
/* * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */#include "xfs.h"#include "xfs_fs.h"#include "xfs_bit.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_clnt.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_alloc.h"#include "xfs_dmapi.h"#include "xfs_quota.h"#include "xfs_mount.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_btree.h"#include "xfs_ialloc.h"#include "xfs_itable.h"#include "xfs_rtalloc.h"#include "xfs_error.h"#include "xfs_bmap.h"#include "xfs_rw.h"#include "xfs_acl.h"#include "xfs_cap.h"#include "xfs_mac.h"#include "xfs_attr.h"#include "xfs_buf_item.h"#include "xfs_trans_space.h"#include "xfs_utils.h"#include "xfs_qm.h"/* * The global quota manager. There is only one of these for the entire * system, _not_ one per file system. XQM keeps track of the overall * quota functionality, including maintaining the freelist and hash * tables of dquots. */mutex_t xfs_Gqm_lock;struct xfs_qm *xfs_Gqm;uint ndquot;kmem_zone_t *qm_dqzone;kmem_zone_t *qm_dqtrxzone;STATIC kmem_shaker_t xfs_qm_shaker;STATIC void xfs_qm_list_init(xfs_dqlist_t *, char *, int);STATIC void xfs_qm_list_destroy(xfs_dqlist_t *);STATIC void xfs_qm_freelist_init(xfs_frlist_t *);STATIC void xfs_qm_freelist_destroy(xfs_frlist_t *);STATIC int xfs_qm_mplist_nowait(xfs_mount_t *);STATIC int xfs_qm_dqhashlock_nowait(xfs_dquot_t *);STATIC int xfs_qm_init_quotainos(xfs_mount_t *);STATIC int xfs_qm_init_quotainfo(xfs_mount_t *);STATIC int xfs_qm_shake(int, gfp_t);#ifdef DEBUGextern mutex_t qcheck_lock;#endif#ifdef QUOTADEBUG#define XQM_LIST_PRINT(l, NXT, title) \{ \ xfs_dquot_t *dqp; int i = 0; \ cmn_err(CE_DEBUG, "%s (#%d)", title, (int) (l)->qh_nelems); \ for (dqp = (l)->qh_next; dqp != NULL; dqp = dqp->NXT) { \ cmn_err(CE_DEBUG, " %d. \"%d (%s)\" " \ "bcnt = %d, icnt = %d, refs = %d", \ ++i, (int) be32_to_cpu(dqp->q_core.d_id), \ DQFLAGTO_TYPESTR(dqp), \ (int) be64_to_cpu(dqp->q_core.d_bcount), \ (int) be64_to_cpu(dqp->q_core.d_icount), \ (int) dqp->q_nrefs); } \}#else#define XQM_LIST_PRINT(l, NXT, title) do { } while (0)#endif/* * Initialize the XQM structure. * Note that there is not one quota manager per file system. */STATIC struct xfs_qm *xfs_Gqm_init(void){ xfs_dqhash_t *udqhash, *gdqhash; xfs_qm_t *xqm; uint i, hsize, flags = KM_SLEEP | KM_MAYFAIL; /* * Initialize the dquot hash tables. */ hsize = XFS_QM_HASHSIZE_HIGH; while (!(udqhash = kmem_zalloc(hsize * sizeof(xfs_dqhash_t), flags))) { if ((hsize >>= 1) <= XFS_QM_HASHSIZE_LOW) flags = KM_SLEEP; } gdqhash = kmem_zalloc(hsize * sizeof(xfs_dqhash_t), KM_SLEEP); ndquot = hsize << 8; xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP); xqm->qm_dqhashmask = hsize - 1; xqm->qm_usr_dqhtable = udqhash; xqm->qm_grp_dqhtable = gdqhash; ASSERT(xqm->qm_usr_dqhtable != NULL); ASSERT(xqm->qm_grp_dqhtable != NULL); for (i = 0; i < hsize; i++) { xfs_qm_list_init(&(xqm->qm_usr_dqhtable[i]), "uxdqh", i); xfs_qm_list_init(&(xqm->qm_grp_dqhtable[i]), "gxdqh", i); } /* * Freelist of all dquots of all file systems */ xfs_qm_freelist_init(&(xqm->qm_dqfreelist)); /* * dquot zone. we register our own low-memory callback. */ if (!qm_dqzone) { xqm->qm_dqzone = kmem_zone_init(sizeof(xfs_dquot_t), "xfs_dquots"); qm_dqzone = xqm->qm_dqzone; } else xqm->qm_dqzone = qm_dqzone; xfs_qm_shaker = kmem_shake_register(xfs_qm_shake); /* * The t_dqinfo portion of transactions. */ if (!qm_dqtrxzone) { xqm->qm_dqtrxzone = kmem_zone_init(sizeof(xfs_dquot_acct_t), "xfs_dqtrx"); qm_dqtrxzone = xqm->qm_dqtrxzone; } else xqm->qm_dqtrxzone = qm_dqtrxzone; atomic_set(&xqm->qm_totaldquots, 0); xqm->qm_dqfree_ratio = XFS_QM_DQFREE_RATIO; xqm->qm_nrefs = 0;#ifdef DEBUG mutex_init(&qcheck_lock, MUTEX_DEFAULT, "qchk");#endif return xqm;}/* * Destroy the global quota manager when its reference count goes to zero. */STATIC voidxfs_qm_destroy( struct xfs_qm *xqm){ int hsize, i; ASSERT(xqm != NULL); ASSERT(xqm->qm_nrefs == 0); kmem_shake_deregister(xfs_qm_shaker); hsize = xqm->qm_dqhashmask + 1; for (i = 0; i < hsize; i++) { xfs_qm_list_destroy(&(xqm->qm_usr_dqhtable[i])); xfs_qm_list_destroy(&(xqm->qm_grp_dqhtable[i])); } kmem_free(xqm->qm_usr_dqhtable, hsize * sizeof(xfs_dqhash_t)); kmem_free(xqm->qm_grp_dqhtable, hsize * sizeof(xfs_dqhash_t)); xqm->qm_usr_dqhtable = NULL; xqm->qm_grp_dqhtable = NULL; xqm->qm_dqhashmask = 0; xfs_qm_freelist_destroy(&(xqm->qm_dqfreelist));#ifdef DEBUG mutex_destroy(&qcheck_lock);#endif kmem_free(xqm, sizeof(xfs_qm_t));}/* * Called at mount time to let XQM know that another file system is * starting quotas. This isn't crucial information as the individual mount * structures are pretty independent, but it helps the XQM keep a * global view of what's going on. *//* ARGSUSED */STATIC intxfs_qm_hold_quotafs_ref( struct xfs_mount *mp){ /* * Need to lock the xfs_Gqm structure for things like this. For example, * the structure could disappear between the entry to this routine and * a HOLD operation if not locked. */ XFS_QM_LOCK(xfs_Gqm); if (xfs_Gqm == NULL) xfs_Gqm = xfs_Gqm_init(); /* * We can keep a list of all filesystems with quotas mounted for * debugging and statistical purposes, but ... * Just take a reference and get out. */ XFS_QM_HOLD(xfs_Gqm); XFS_QM_UNLOCK(xfs_Gqm); return 0;}/* * Release the reference that a filesystem took at mount time, * so that we know when we need to destroy the entire quota manager. *//* ARGSUSED */STATIC voidxfs_qm_rele_quotafs_ref( struct xfs_mount *mp){ xfs_dquot_t *dqp, *nextdqp; ASSERT(xfs_Gqm); ASSERT(xfs_Gqm->qm_nrefs > 0); /* * Go thru the freelist and destroy all inactive dquots. */ xfs_qm_freelist_lock(xfs_Gqm); for (dqp = xfs_Gqm->qm_dqfreelist.qh_next; dqp != (xfs_dquot_t *)&(xfs_Gqm->qm_dqfreelist); ) { xfs_dqlock(dqp); nextdqp = dqp->dq_flnext; 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); xfs_qm_dqdestroy(dqp); } else { xfs_dqunlock(dqp); } dqp = nextdqp; } xfs_qm_freelist_unlock(xfs_Gqm); /* * Destroy the entire XQM. If somebody mounts with quotaon, this'll * be restarted. */ XFS_QM_LOCK(xfs_Gqm); XFS_QM_RELE(xfs_Gqm); if (xfs_Gqm->qm_nrefs == 0) { xfs_qm_destroy(xfs_Gqm); xfs_Gqm = NULL; } XFS_QM_UNLOCK(xfs_Gqm);}/* * This is called at mount time from xfs_mountfs to initialize the quotainfo * structure and start the global quotamanager (xfs_Gqm) if it hasn't done * so already. Note that the superblock has not been read in yet. */voidxfs_qm_mount_quotainit( xfs_mount_t *mp, uint flags){ /* * User, projects or group quotas has to be on. */ ASSERT(flags & (XFSMNT_UQUOTA | XFSMNT_PQUOTA | XFSMNT_GQUOTA)); /* * Initialize the flags in the mount structure. From this point * onwards we look at m_qflags to figure out if quotas's ON/OFF, etc. * Note that we enforce nothing if accounting is off. * ie. XFSMNT_*QUOTA must be ON for XFSMNT_*QUOTAENF. * It isn't necessary to take the quotaoff lock to do this; this is * called from mount. */ if (flags & XFSMNT_UQUOTA) { mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE); if (flags & XFSMNT_UQUOTAENF) mp->m_qflags |= XFS_UQUOTA_ENFD; } if (flags & XFSMNT_GQUOTA) { mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE); if (flags & XFSMNT_GQUOTAENF) mp->m_qflags |= XFS_OQUOTA_ENFD; } else if (flags & XFSMNT_PQUOTA) { mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE); if (flags & XFSMNT_PQUOTAENF) mp->m_qflags |= XFS_OQUOTA_ENFD; }}/* * Just destroy the quotainfo structure. */voidxfs_qm_unmount_quotadestroy( xfs_mount_t *mp){ if (mp->m_quotainfo) xfs_qm_destroy_quotainfo(mp);}/* * 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. */intxfs_qm_mount_quotas( xfs_mount_t *mp, int mfsi_flags){ unsigned long s; int error = 0; uint sbf; /* * If quotas on realtime volumes is not supported, we disable * quotas immediately. */ if (mp->m_sb.sb_rextents) { cmn_err(CE_NOTE, "Cannot turn on quotas for realtime filesystem %s", mp->m_fsname); 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. */ if ((error = xfs_qm_init_quotainfo(mp))) { /* * 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) && !(mfsi_flags & XFS_MFSI_NO_QUOTACHECK)) { if ((error = xfs_qm_quotacheck(mp))) { /* Quotacheck has failed and quotas have * been disabled. */ return XFS_ERROR(error); } } write_changes: /* * We actually don't have to acquire the SB_LOCK at all. * This can only be called from mount, and that's single threaded. XXX */ s = XFS_SB_LOCK(mp); sbf = mp->m_sb.sb_qflags; mp->m_sb.sb_qflags = mp->m_qflags & XFS_MOUNT_QUOTA_ALL; XFS_SB_UNLOCK(mp, s); 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_fs_cmn_err(CE_ALERT, mp, "XFS mount_quotas: Superblock update failed!"); } } if (error) { xfs_fs_cmn_err(CE_WARN, mp, "Failed to initialize disk quotas."); } return XFS_ERROR(error);}/* * Called from the vfsops layer. */intxfs_qm_unmount_quotas( xfs_mount_t *mp){ xfs_inode_t *uqp, *gqp; int error = 0; /* * Release the dquots that root inode, et al might be holding, * before we flush quotas and blow away the quotainfo structure. */ ASSERT(mp->m_rootip); xfs_qm_dqdetach(mp->m_rootip); if (mp->m_rbmip) xfs_qm_dqdetach(mp->m_rbmip); if (mp->m_rsumip) xfs_qm_dqdetach(mp->m_rsumip); /* * Flush out the quota inodes. */ uqp = gqp = NULL; if (mp->m_quotainfo) { if ((uqp = mp->m_quotainfo->qi_uquotaip) != NULL) { xfs_ilock(uqp, XFS_ILOCK_EXCL); xfs_iflock(uqp); error = xfs_iflush(uqp, XFS_IFLUSH_SYNC); xfs_iunlock(uqp, XFS_ILOCK_EXCL); if (unlikely(error == EFSCORRUPTED)) { XFS_ERROR_REPORT("xfs_qm_unmount_quotas(1)", XFS_ERRLEVEL_LOW, mp); goto out; } } if ((gqp = mp->m_quotainfo->qi_gquotaip) != NULL) { xfs_ilock(gqp, XFS_ILOCK_EXCL); xfs_iflock(gqp); error = xfs_iflush(gqp, XFS_IFLUSH_SYNC); xfs_iunlock(gqp, XFS_ILOCK_EXCL); if (unlikely(error == EFSCORRUPTED)) { XFS_ERROR_REPORT("xfs_qm_unmount_quotas(2)", XFS_ERRLEVEL_LOW, mp); goto out; } } } if (uqp) { XFS_PURGE_INODE(uqp); mp->m_quotainfo->qi_uquotaip = NULL; } if (gqp) { XFS_PURGE_INODE(gqp); mp->m_quotainfo->qi_gquotaip = NULL; }out: return XFS_ERROR(error);}/* * Flush all dquots of the given file system to disk. The dquots are * _not_ purged from memory here, just their data written to disk. */STATIC intxfs_qm_dqflush_all( xfs_mount_t *mp, int flags){ int recl; xfs_dquot_t *dqp; int niters; int error; if (mp->m_quotainfo == NULL) return (0); niters = 0;again: xfs_qm_mplist_lock(mp); FOREACH_DQUOT_IN_MP(dqp, mp) { xfs_dqlock(dqp); if (! XFS_DQ_IS_DIRTY(dqp)) { xfs_dqunlock(dqp); continue; } xfs_dqtrace_entry(dqp, "FLUSHALL: DQDIRTY"); /* XXX a sentinel would be better */ recl = XFS_QI_MPLRECLAIMS(mp); if (! xfs_qm_dqflock_nowait(dqp)) { /* * If we can't grab the flush lock then check * to see if the dquot has been flushed delayed * write. If so, grab its buffer and send it * out immediately. We'll be able to acquire * the flush lock when the I/O completes.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -