📄 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 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_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_bmap.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. */STATIC 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; dqp->q_core.d_id = cpu_to_be32(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 = NULL; 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. */ d->dd_diskdq.d_magic = cpu_to_be16(XFS_DQUOT_MAGIC); d->dd_diskdq.d_version = XFS_DQUOT_VERSION; d->dd_diskdq.d_id = cpu_to_be32(id); d->dd_diskdq.d_flags = 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)be64_to_cpu(dqp->q_core.d_bcount), (void *)(__psint_t)be64_to_cpu(dqp->q_core.d_icount), (void *)(__psint_t)be64_to_cpu(dqp->q_core.d_blk_hardlimit), (void *)(__psint_t)be64_to_cpu(dqp->q_core.d_blk_softlimit), (void *)(__psint_t)be64_to_cpu(dqp->q_core.d_ino_hardlimit), (void *)(__psint_t)be64_to_cpu(dqp->q_core.d_ino_softlimit), (void *)(__psint_t)be32_to_cpu(dqp->q_core.d_id), (void *)(__psint_t)current_pid(), (void *)(__psint_t)ino, (void *)(__psint_t)retaddr, (void *)(__psint_t)udqp); return;}#endif/* * If default limits are in force, push them into the dquot now. * We overwrite the dquot limits only if they are zero and this * is not the root dquot. */voidxfs_qm_adjust_dqlimits( xfs_mount_t *mp, xfs_disk_dquot_t *d){ xfs_quotainfo_t *q = mp->m_quotainfo; ASSERT(d->d_id); if (q->qi_bsoftlimit && !d->d_blk_softlimit) d->d_blk_softlimit = cpu_to_be64(q->qi_bsoftlimit); if (q->qi_bhardlimit && !d->d_blk_hardlimit) d->d_blk_hardlimit = cpu_to_be64(q->qi_bhardlimit); if (q->qi_isoftlimit && !d->d_ino_softlimit) d->d_ino_softlimit = cpu_to_be64(q->qi_isoftlimit); if (q->qi_ihardlimit && !d->d_ino_hardlimit) d->d_ino_hardlimit = cpu_to_be64(q->qi_ihardlimit); if (q->qi_rtbsoftlimit && !d->d_rtb_softlimit) d->d_rtb_softlimit = cpu_to_be64(q->qi_rtbsoftlimit); if (q->qi_rtbhardlimit && !d->d_rtb_hardlimit) d->d_rtb_hardlimit = cpu_to_be64(q->qi_rtbhardlimit);}/* * 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. They do * get reset to zero, however, when we find the count to be under * the soft limit (they are only ever set non-zero via userspace). */voidxfs_qm_adjust_dqtimers( xfs_mount_t *mp, xfs_disk_dquot_t *d){ ASSERT(d->d_id);#ifdef QUOTADEBUG if (d->d_blk_hardlimit) ASSERT(be64_to_cpu(d->d_blk_softlimit) <= be64_to_cpu(d->d_blk_hardlimit)); if (d->d_ino_hardlimit) ASSERT(be64_to_cpu(d->d_ino_softlimit) <= be64_to_cpu(d->d_ino_hardlimit)); if (d->d_rtb_hardlimit) ASSERT(be64_to_cpu(d->d_rtb_softlimit) <= be64_to_cpu(d->d_rtb_hardlimit));#endif if (!d->d_btimer) { if ((d->d_blk_softlimit && (be64_to_cpu(d->d_bcount) >= be64_to_cpu(d->d_blk_softlimit))) || (d->d_blk_hardlimit && (be64_to_cpu(d->d_bcount) >= be64_to_cpu(d->d_blk_hardlimit)))) { d->d_btimer = cpu_to_be32(get_seconds() + XFS_QI_BTIMELIMIT(mp)); } else { d->d_bwarns = 0; } } else { if ((!d->d_blk_softlimit || (be64_to_cpu(d->d_bcount) < be64_to_cpu(d->d_blk_softlimit))) && (!d->d_blk_hardlimit || (be64_to_cpu(d->d_bcount) < be64_to_cpu(d->d_blk_hardlimit)))) { d->d_btimer = 0; } } if (!d->d_itimer) { if ((d->d_ino_softlimit && (be64_to_cpu(d->d_icount) >= be64_to_cpu(d->d_ino_softlimit))) || (d->d_ino_hardlimit && (be64_to_cpu(d->d_icount) >= be64_to_cpu(d->d_ino_hardlimit)))) { d->d_itimer = cpu_to_be32(get_seconds() + XFS_QI_ITIMELIMIT(mp)); } else { d->d_iwarns = 0; } } else { if ((!d->d_ino_softlimit || (be64_to_cpu(d->d_icount) < be64_to_cpu(d->d_ino_softlimit))) && (!d->d_ino_hardlimit || (be64_to_cpu(d->d_icount) < be64_to_cpu(d->d_ino_hardlimit)))) { d->d_itimer = 0; } } if (!d->d_rtbtimer) { if ((d->d_rtb_softlimit && (be64_to_cpu(d->d_rtbcount) >= be64_to_cpu(d->d_rtb_softlimit))) || (d->d_rtb_hardlimit && (be64_to_cpu(d->d_rtbcount) >= be64_to_cpu(d->d_rtb_hardlimit)))) { d->d_rtbtimer = cpu_to_be32(get_seconds() + XFS_QI_RTBTIMELIMIT(mp)); } else { d->d_rtbwarns = 0; } } else { if ((!d->d_rtb_softlimit || (be64_to_cpu(d->d_rtbcount) < be64_to_cpu(d->d_rtb_softlimit))) && (!d->d_rtb_hardlimit || (be64_to_cpu(d->d_rtbcount) < be64_to_cpu(d->d_rtb_hardlimit)))) { d->d_rtbtimer = 0; } }}/* * 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 : ((type & XFS_DQ_PROJ) ? XFS_BLI_PDQUOT_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 **tpp, 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; xfs_trans_t *tp = *tpp; 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, be32_to_cpu(dqp->q_core.d_id), dqp->dq_flags & XFS_DQ_ALLTYPES, bp); /* * xfs_bmap_finish() may commit the current transaction and * start a second transaction if the freelist is not empty. * * Since we still want to modify this buffer, we need to * ensure that the buffer is not released on commit of * the first transaction and ensure the buffer is added to the * second transaction. * * If there is only one transaction then don't stop the buffer * from being released when it commits later on. */ xfs_trans_bhold(tp, bp); if ((error = xfs_bmap_finish(tpp, &flist, firstblock, &committed))) { goto error1; } if (committed) { tp = *tpp; xfs_trans_bjoin(tp, bp); } else { xfs_trans_bhold_release(tp, bp); } *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 **tpp, 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; xfs_trans_t *tp = (tpp ? *tpp : NULL); mp = dqp->q_mount;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -