?? xfs_dquot.c
字號:
/* * Copyright (c) 2000-2003 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_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_trans_priv.h"#include "xfs_qm.h"/* LOCK ORDER inode lock (ilock) dquot hash-chain lock (hashlock) xqm dquot freelist lock (freelistlock mount's dquot list lock (mplistlock) user dquot lock - lock ordering among dquots is based on the uid or gid group dquot lock - similar to udquots. Between the two dquots, the udquot has to be locked first. pin lock - the dquot lock must be held to take this lock. flush lock - ditto.*/STATIC void xfs_qm_dqflush_done(xfs_buf_t *, xfs_dq_logitem_t *);#ifdef DEBUGxfs_buftarg_t *xfs_dqerror_target;int xfs_do_dqerror;int xfs_dqreq_num;int xfs_dqerror_mod = 33;#endif/* * Allocate and initialize a dquot. We don't always allocate fresh memory; * we try to reclaim a free dquot if the number of incore dquots are above * a threshold. * The only field inside the core that gets initialized at this point * is the d_id field. The idea is to fill in the entire q_core * when we read in the on disk dquot. */xfs_dquot_t *xfs_qm_dqinit( xfs_mount_t *mp, xfs_dqid_t id, uint type){ xfs_dquot_t *dqp; boolean_t brandnewdquot; brandnewdquot = xfs_qm_dqalloc_incore(&dqp); dqp->dq_flags = type; INT_SET(dqp->q_core.d_id, ARCH_CONVERT, id); dqp->q_mount = mp; /* * No need to re-initialize these if this is a reclaimed dquot. */ if (brandnewdquot) { dqp->dq_flnext = dqp->dq_flprev = dqp; mutex_init(&dqp->q_qlock, MUTEX_DEFAULT, "xdq"); initnsema(&dqp->q_flock, 1, "fdq"); sv_init(&dqp->q_pinwait, SV_DEFAULT, "pdq");#ifdef XFS_DQUOT_TRACE dqp->q_trace = ktrace_alloc(DQUOT_TRACE_SIZE, KM_SLEEP); xfs_dqtrace_entry(dqp, "DQINIT");#endif } else { /* * Only the q_core portion was zeroed in dqreclaim_one(). * So, we need to reset others. */ dqp->q_nrefs = 0; dqp->q_blkno = 0; dqp->MPL_NEXT = dqp->HL_NEXT = NULL; dqp->HL_PREVP = dqp->MPL_PREVP = NULL; dqp->q_bufoffset = 0; dqp->q_fileoffset = 0; dqp->q_transp = NULL; dqp->q_gdquot = NULL; dqp->q_res_bcount = 0; dqp->q_res_icount = 0; dqp->q_res_rtbcount = 0; dqp->q_pincount = 0; dqp->q_hash = 0; ASSERT(dqp->dq_flnext == dqp->dq_flprev);#ifdef XFS_DQUOT_TRACE ASSERT(dqp->q_trace); xfs_dqtrace_entry(dqp, "DQRECLAIMED_INIT");#endif } /* * log item gets initialized later */ return (dqp);}/* * This is called to free all the memory associated with a dquot */voidxfs_qm_dqdestroy( xfs_dquot_t *dqp){ ASSERT(! XFS_DQ_IS_ON_FREELIST(dqp)); mutex_destroy(&dqp->q_qlock); freesema(&dqp->q_flock); sv_destroy(&dqp->q_pinwait);#ifdef XFS_DQUOT_TRACE if (dqp->q_trace) ktrace_free(dqp->q_trace); dqp->q_trace = NULL;#endif kmem_zone_free(xfs_Gqm->qm_dqzone, dqp); atomic_dec(&xfs_Gqm->qm_totaldquots);}/* * This is what a 'fresh' dquot inside a dquot chunk looks like on disk. */STATIC voidxfs_qm_dqinit_core( xfs_dqid_t id, uint type, xfs_dqblk_t *d){ /* * Caller has zero'd the entire dquot 'chunk' already. */ INT_SET(d->dd_diskdq.d_magic, ARCH_CONVERT, XFS_DQUOT_MAGIC); INT_SET(d->dd_diskdq.d_version, ARCH_CONVERT, XFS_DQUOT_VERSION); INT_SET(d->dd_diskdq.d_id, ARCH_CONVERT, id); INT_SET(d->dd_diskdq.d_flags, ARCH_CONVERT, type);}#ifdef XFS_DQUOT_TRACE/* * Dquot tracing for debugging. *//* ARGSUSED */void__xfs_dqtrace_entry( xfs_dquot_t *dqp, char *func, void *retaddr, xfs_inode_t *ip){ xfs_dquot_t *udqp = NULL; xfs_ino_t ino = 0; ASSERT(dqp->q_trace); if (ip) { ino = ip->i_ino; udqp = ip->i_udquot; } ktrace_enter(dqp->q_trace, (void *)(__psint_t)DQUOT_KTRACE_ENTRY, (void *)func, (void *)(__psint_t)dqp->q_nrefs, (void *)(__psint_t)dqp->dq_flags, (void *)(__psint_t)dqp->q_res_bcount, (void *)(__psint_t)INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), (void *)(__psint_t)INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), (void *)(__psint_t)INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT), (void *)(__psint_t)INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT), (void *)(__psint_t)INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT), (void *)(__psint_t)INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT), (void *)(__psint_t)INT_GET(dqp->q_core.d_id, ARCH_CONVERT), (void *)(__psint_t)current_pid(), (void *)(__psint_t)ino, (void *)(__psint_t)retaddr, (void *)(__psint_t)udqp); return;}#endif/* * Check the limits and timers of a dquot and start or reset timers * if necessary. * This gets called even when quota enforcement is OFF, which makes our * life a little less complicated. (We just don't reject any quota * reservations in that case, when enforcement is off). * We also return 0 as the values of the timers in Q_GETQUOTA calls, when * enforcement's off. * In contrast, warnings are a little different in that they don't * 'automatically' get started when limits get exceeded. */voidxfs_qm_adjust_dqtimers( xfs_mount_t *mp, xfs_disk_dquot_t *d){ /* * The dquot had better be locked. We are modifying it here. */ /* * root's limits are not real limits. */ if (INT_ISZERO(d->d_id, ARCH_CONVERT)) return;#ifdef QUOTADEBUG if (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)) ASSERT(INT_GET(d->d_blk_softlimit, ARCH_CONVERT) <= INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)); if (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)) ASSERT(INT_GET(d->d_ino_softlimit, ARCH_CONVERT) <= INT_GET(d->d_ino_hardlimit, ARCH_CONVERT));#endif if (INT_ISZERO(d->d_btimer, ARCH_CONVERT)) { if ((INT_GET(d->d_blk_softlimit, ARCH_CONVERT) && (INT_GET(d->d_bcount, ARCH_CONVERT) >= INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) || (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT) && (INT_GET(d->d_bcount, ARCH_CONVERT) >= INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { INT_SET(d->d_btimer, ARCH_CONVERT, get_seconds() + XFS_QI_BTIMELIMIT(mp)); } } else { if ((INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT) || (INT_GET(d->d_bcount, ARCH_CONVERT) < INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) && (INT_ISZERO(d->d_blk_hardlimit, ARCH_CONVERT) || (INT_GET(d->d_bcount, ARCH_CONVERT) < INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { INT_ZERO(d->d_btimer, ARCH_CONVERT); } } if (INT_ISZERO(d->d_itimer, ARCH_CONVERT)) { if ((INT_GET(d->d_ino_softlimit, ARCH_CONVERT) && (INT_GET(d->d_icount, ARCH_CONVERT) >= INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) || (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT) && (INT_GET(d->d_icount, ARCH_CONVERT) >= INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { INT_SET(d->d_itimer, ARCH_CONVERT, get_seconds() + XFS_QI_ITIMELIMIT(mp)); } } else { if ((INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT) || (INT_GET(d->d_icount, ARCH_CONVERT) < INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) && (INT_ISZERO(d->d_ino_hardlimit, ARCH_CONVERT) || (INT_GET(d->d_icount, ARCH_CONVERT) < INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { INT_ZERO(d->d_itimer, ARCH_CONVERT); } }}/* * Increment or reset warnings of a given dquot. */intxfs_qm_dqwarn( xfs_disk_dquot_t *d, uint flags){ int warned; /* * root's limits are not real limits. */ if (INT_ISZERO(d->d_id, ARCH_CONVERT)) return (0); warned = 0; if (INT_GET(d->d_blk_softlimit, ARCH_CONVERT) && (INT_GET(d->d_bcount, ARCH_CONVERT) >= INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) { if (flags & XFS_QMOPT_DOWARN) { INT_MOD(d->d_bwarns, ARCH_CONVERT, +1); warned++; } } else { if (INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT) || (INT_GET(d->d_bcount, ARCH_CONVERT) < INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) { INT_ZERO(d->d_bwarns, ARCH_CONVERT); } } if (INT_GET(d->d_ino_softlimit, ARCH_CONVERT) > 0 && (INT_GET(d->d_icount, ARCH_CONVERT) >= INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) { if (flags & XFS_QMOPT_DOWARN) { INT_MOD(d->d_iwarns, ARCH_CONVERT, +1); warned++; } } else { if ((INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT)) || (INT_GET(d->d_icount, ARCH_CONVERT) < INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) { INT_ZERO(d->d_iwarns, ARCH_CONVERT); } }#ifdef QUOTADEBUG if (INT_GET(d->d_iwarns, ARCH_CONVERT)) cmn_err(CE_DEBUG, "--------@@Inode warnings running : %Lu >= %Lu", INT_GET(d->d_icount, ARCH_CONVERT), INT_GET(d->d_ino_softlimit, ARCH_CONVERT)); if (INT_GET(d->d_bwarns, ARCH_CONVERT)) cmn_err(CE_DEBUG, "--------@@Blks warnings running : %Lu >= %Lu", INT_GET(d->d_bcount, ARCH_CONVERT), INT_GET(d->d_blk_softlimit, ARCH_CONVERT));#endif return (warned);}/* * initialize a buffer full of dquots and log the whole thing */STATIC voidxfs_qm_init_dquot_blk( xfs_trans_t *tp, xfs_mount_t *mp, xfs_dqid_t id, uint type, xfs_buf_t *bp){ xfs_dqblk_t *d; int curid, i; ASSERT(tp); ASSERT(XFS_BUF_ISBUSY(bp)); ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); d = (xfs_dqblk_t *)XFS_BUF_PTR(bp); /* * ID of the first dquot in the block - id's are zero based. */ curid = id - (id % XFS_QM_DQPERBLK(mp)); ASSERT(curid >= 0); memset(d, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp))); for (i = 0; i < XFS_QM_DQPERBLK(mp); i++, d++, curid++) xfs_qm_dqinit_core(curid, type, d); xfs_trans_dquot_buf(tp, bp, type & XFS_DQ_USER ? XFS_BLI_UDQUOT_BUF : XFS_BLI_GDQUOT_BUF); xfs_trans_log_buf(tp, bp, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1);}/* * Allocate a block and fill it with dquots. * This is called when the bmapi finds a hole. */STATIC intxfs_qm_dqalloc( xfs_trans_t *tp, xfs_mount_t *mp, xfs_dquot_t *dqp, xfs_inode_t *quotip, xfs_fileoff_t offset_fsb, xfs_buf_t **O_bpp){ xfs_fsblock_t firstblock; xfs_bmap_free_t flist; xfs_bmbt_irec_t map; int nmaps, error, committed; xfs_buf_t *bp; ASSERT(tp != NULL); xfs_dqtrace_entry(dqp, "DQALLOC"); /* * Initialize the bmap freelist prior to calling bmapi code. */ XFS_BMAP_INIT(&flist, &firstblock); xfs_ilock(quotip, XFS_ILOCK_EXCL); /* * Return if this type of quotas is turned off while we didn't * have an inode lock */ if (XFS_IS_THIS_QUOTA_OFF(dqp)) { xfs_iunlock(quotip, XFS_ILOCK_EXCL); return (ESRCH); } /* * xfs_trans_commit normally decrements the vnode ref count * when it unlocks the inode. Since we want to keep the quota * inode around, we bump the vnode ref count now. */ VN_HOLD(XFS_ITOV(quotip)); xfs_trans_ijoin(tp, quotip, XFS_ILOCK_EXCL); nmaps = 1; if ((error = xfs_bmapi(tp, quotip, offset_fsb, XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA | XFS_BMAPI_WRITE, &firstblock, XFS_QM_DQALLOC_SPACE_RES(mp), &map, &nmaps, &flist))) { goto error0; } ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB); ASSERT(nmaps == 1); ASSERT((map.br_startblock != DELAYSTARTBLOCK) && (map.br_startblock != HOLESTARTBLOCK)); /* * Keep track of the blkno to save a lookup later */ dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock); /* now we can just get the buffer (there's nothing to read yet) */ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, dqp->q_blkno, XFS_QI_DQCHUNKLEN(mp), 0); if (!bp || (error = XFS_BUF_GETERROR(bp))) goto error1; /* * Make a chunk of dquots out of this buffer and log * the entire thing. */ xfs_qm_init_dquot_blk(tp, mp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT), dqp->dq_flags & (XFS_DQ_USER|XFS_DQ_GROUP), bp); if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed))) { goto error1; } *O_bpp = bp; return 0; error1: xfs_bmap_cancel(&flist); error0: xfs_iunlock(quotip, XFS_ILOCK_EXCL); return (error);}/* * Maps a dquot to the buffer containing its on-disk version. * This returns a ptr to the buffer containing the on-disk dquot * in the bpp param, and a ptr to the on-disk dquot within that buffer */STATIC intxfs_qm_dqtobp( xfs_trans_t *tp, xfs_dquot_t *dqp, xfs_disk_dquot_t **O_ddpp, xfs_buf_t **O_bpp, uint flags){ xfs_bmbt_irec_t map; int nmaps, error; xfs_buf_t *bp; xfs_inode_t *quotip; xfs_mount_t *mp; xfs_disk_dquot_t *ddq; xfs_dqid_t id; boolean_t newdquot; mp = dqp->q_mount;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -