?? xfs_qm.c
字號:
/* * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 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. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU General Public License along * with this program; if not, write the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ */#include "xfs.h"#include "xfs_fs.h"#include "xfs_inum.h"#include "xfs_log.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_alloc_btree.h"#include "xfs_bmap_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_btree.h"#include "xfs_ialloc.h"#include "xfs_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_bmap.h"#include "xfs_bit.h"#include "xfs_rtalloc.h"#include "xfs_error.h"#include "xfs_itable.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;kmem_zone_t *qm_dqzone;kmem_zone_t *qm_dqtrxzone;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 int xfs_qm_quotacheck(xfs_mount_t *);STATIC int xfs_qm_init_quotainos(xfs_mount_t *);STATIC int xfs_qm_shake(int, unsigned int);#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) INT_GET(dqp->q_core.d_id, ARCH_CONVERT), \ DQFLAGTO_TYPESTR(dqp), \ (int) INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), \ (int) INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), \ (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_qm_t *xqm; int hsize, i; xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP); ASSERT(xqm); /* * Initialize the dquot hash tables. */ hsize = (DQUOT_HASH_HEURISTIC < XFS_QM_NCSIZE_THRESHOLD) ? XFS_QM_HASHSIZE_LOW : XFS_QM_HASHSIZE_HIGH; xqm->qm_dqhashmask = hsize - 1; xqm->qm_usr_dqhtable = (xfs_dqhash_t *)kmem_zalloc(hsize * sizeof(xfs_dqhash_t), KM_SLEEP); xqm->qm_grp_dqhtable = (xfs_dqhash_t *)kmem_zalloc(hsize * sizeof(xfs_dqhash_t), KM_SLEEP); 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. */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) { if ((xfs_Gqm = xfs_Gqm_init()) == NULL) { return (XFS_ERROR(EINVAL)); } } /* * 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 or group quotas has to be on. */ ASSERT(flags & (XFSMNT_UQUOTA | 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_GQUOTA_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){ unsigned long s; int error = 0; uint sbf; /* * If a file system had quotas running earlier, but decided to * mount without -o quota/uquota/gquota options, revoke the * quotachecked license, and bail out. */ if (! XFS_IS_QUOTA_ON(mp) && (mp->m_sb.sb_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT))) { mp->m_qflags = 0; goto write_changes; } /* * 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; }#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) cmn_err(CE_NOTE, "Attempting to turn on disk quotas.");#endif 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)) {#ifdef DEBUG cmn_err(CE_NOTE, "Doing a quotacheck. Please wait.");#endif if ((error = xfs_qm_quotacheck(mp))) { cmn_err(CE_WARN, "Quotacheck unsuccessful (Error %d): " "Disabling quotas.", error); /* * We must turn off quotas. */ ASSERT(mp->m_quotainfo != NULL); ASSERT(xfs_Gqm != NULL); xfs_qm_destroy_quotainfo(mp); mp->m_qflags = 0; goto write_changes; }#ifdef DEBUG cmn_err(CE_NOTE, "Done quotacheck.");#endif } 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. */intxfs_qm_dqflush_all(
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -