📄 xfs_qm.c
字号:
return (error);}/* * Iterate over all allocated USR/GRP/PRJ dquots in the system, calling a * caller supplied function for every chunk of dquots that we find. */STATIC intxfs_qm_dqiterate( xfs_mount_t *mp, xfs_inode_t *qip, uint flags){ xfs_bmbt_irec_t *map; int i, nmaps; /* number of map entries */ int error; /* return value */ xfs_fileoff_t lblkno; xfs_filblks_t maxlblkcnt; xfs_dqid_t firstid; xfs_fsblock_t rablkno; xfs_filblks_t rablkcnt; error = 0; /* * This looks racey, but we can't keep an inode lock across a * trans_reserve. But, this gets called during quotacheck, and that * happens only at mount time which is single threaded. */ if (qip->i_d.di_nblocks == 0) return (0); map = kmem_alloc(XFS_DQITER_MAP_SIZE * sizeof(*map), KM_SLEEP); lblkno = 0; maxlblkcnt = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp)); do { nmaps = XFS_DQITER_MAP_SIZE; /* * We aren't changing the inode itself. Just changing * some of its data. No new blocks are added here, and * the inode is never added to the transaction. */ xfs_ilock(qip, XFS_ILOCK_SHARED); error = xfs_bmapi(NULL, qip, lblkno, maxlblkcnt - lblkno, XFS_BMAPI_METADATA, NULL, 0, map, &nmaps, NULL); xfs_iunlock(qip, XFS_ILOCK_SHARED); if (error) break; ASSERT(nmaps <= XFS_DQITER_MAP_SIZE); for (i = 0; i < nmaps; i++) { ASSERT(map[i].br_startblock != DELAYSTARTBLOCK); ASSERT(map[i].br_blockcount); lblkno += map[i].br_blockcount; if (map[i].br_startblock == HOLESTARTBLOCK) continue; firstid = (xfs_dqid_t) map[i].br_startoff * XFS_QM_DQPERBLK(mp); /* * Do a read-ahead on the next extent. */ if ((i+1 < nmaps) && (map[i+1].br_startblock != HOLESTARTBLOCK)) { rablkcnt = map[i+1].br_blockcount; rablkno = map[i+1].br_startblock; while (rablkcnt--) { xfs_baread(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, rablkno), (int)XFS_QI_DQCHUNKLEN(mp)); rablkno++; } } /* * Iterate thru all the blks in the extent and * reset the counters of all the dquots inside them. */ if ((error = xfs_qm_dqiter_bufs(mp, firstid, map[i].br_startblock, map[i].br_blockcount, flags))) { break; } } if (error) break; } while (nmaps > 0); kmem_free(map, XFS_DQITER_MAP_SIZE * sizeof(*map)); return (error);}/* * Called by dqusage_adjust in doing a quotacheck. * Given the inode, and a dquot (either USR or GRP, doesn't matter), * this updates its incore copy as well as the buffer copy. This is * so that once the quotacheck is done, we can just log all the buffers, * as opposed to logging numerous updates to individual dquots. */STATIC voidxfs_qm_quotacheck_dqadjust( xfs_dquot_t *dqp, xfs_qcnt_t nblks, xfs_qcnt_t rtblks){ ASSERT(XFS_DQ_IS_LOCKED(dqp)); xfs_dqtrace_entry(dqp, "QCHECK DQADJUST"); /* * Adjust the inode count and the block count to reflect this inode's * resource usage. */ be64_add(&dqp->q_core.d_icount, 1); dqp->q_res_icount++; if (nblks) { be64_add(&dqp->q_core.d_bcount, nblks); dqp->q_res_bcount += nblks; } if (rtblks) { be64_add(&dqp->q_core.d_rtbcount, rtblks); dqp->q_res_rtbcount += rtblks; } /* * Set default limits, adjust timers (since we changed usages) */ if (! XFS_IS_SUSER_DQUOT(dqp)) { xfs_qm_adjust_dqlimits(dqp->q_mount, &dqp->q_core); xfs_qm_adjust_dqtimers(dqp->q_mount, &dqp->q_core); } dqp->dq_flags |= XFS_DQ_DIRTY;}STATIC intxfs_qm_get_rtblks( xfs_inode_t *ip, xfs_qcnt_t *O_rtblks){ xfs_filblks_t rtblks; /* total rt blks */ xfs_ifork_t *ifp; /* inode fork pointer */ xfs_extnum_t nextents; /* number of extent entries */ xfs_bmbt_rec_t *base; /* base of extent array */ xfs_bmbt_rec_t *ep; /* pointer to an extent entry */ int error; ASSERT(XFS_IS_REALTIME_INODE(ip)); ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); if (!(ifp->if_flags & XFS_IFEXTENTS)) { if ((error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK))) return (error); } rtblks = 0; nextents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); base = &ifp->if_u1.if_extents[0]; for (ep = base; ep < &base[nextents]; ep++) rtblks += xfs_bmbt_get_blockcount(ep); *O_rtblks = (xfs_qcnt_t)rtblks; return (0);}/* * callback routine supplied to bulkstat(). Given an inumber, find its * dquots and update them to account for resources taken by that inode. *//* ARGSUSED */STATIC intxfs_qm_dqusage_adjust( xfs_mount_t *mp, /* mount point for filesystem */ xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* not used */ int ubsize, /* not used */ void *private_data, /* not used */ xfs_daddr_t bno, /* starting block of inode cluster */ int *ubused, /* not used */ void *dip, /* on-disk inode pointer (not used) */ int *res) /* result code value */{ xfs_inode_t *ip; xfs_dquot_t *udqp, *gdqp; xfs_qcnt_t nblks, rtblks; int error; ASSERT(XFS_IS_QUOTA_RUNNING(mp)); /* * rootino must have its resources accounted for, not so with the quota * inodes. */ if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) { *res = BULKSTAT_RV_NOTHING; return XFS_ERROR(EINVAL); } /* * We don't _need_ to take the ilock EXCL. However, the xfs_qm_dqget * interface expects the inode to be exclusively locked because that's * the case in all other instances. It's OK that we do this because * quotacheck is done only at mount time. */ if ((error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip, bno))) { *res = BULKSTAT_RV_NOTHING; return (error); } if (ip->i_d.di_mode == 0) { xfs_iput_new(ip, XFS_ILOCK_EXCL); *res = BULKSTAT_RV_NOTHING; return XFS_ERROR(ENOENT); } /* * Obtain the locked dquots. In case of an error (eg. allocation * fails for ENOSPC), we return the negative of the error number * to bulkstat, so that it can get propagated to quotacheck() and * making us disable quotas for the file system. */ if ((error = xfs_qm_dqget_noattach(ip, &udqp, &gdqp))) { xfs_iput(ip, XFS_ILOCK_EXCL); *res = BULKSTAT_RV_GIVEUP; return (error); } rtblks = 0; if (! XFS_IS_REALTIME_INODE(ip)) { nblks = (xfs_qcnt_t)ip->i_d.di_nblocks; } else { /* * Walk thru the extent list and count the realtime blocks. */ if ((error = xfs_qm_get_rtblks(ip, &rtblks))) { xfs_iput(ip, XFS_ILOCK_EXCL); if (udqp) xfs_qm_dqput(udqp); if (gdqp) xfs_qm_dqput(gdqp); *res = BULKSTAT_RV_GIVEUP; return (error); } nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks; } ASSERT(ip->i_delayed_blks == 0); /* * We can't release the inode while holding its dquot locks. * The inode can go into inactive and might try to acquire the dquotlocks. * So, just unlock here and do a vn_rele at the end. */ xfs_iunlock(ip, XFS_ILOCK_EXCL); /* * Add the (disk blocks and inode) resources occupied by this * inode to its dquots. We do this adjustment in the incore dquot, * and also copy the changes to its buffer. * We don't care about putting these changes in a transaction * envelope because if we crash in the middle of a 'quotacheck' * we have to start from the beginning anyway. * Once we're done, we'll log all the dquot bufs. * * The *QUOTA_ON checks below may look pretty racey, but quotachecks * and quotaoffs don't race. (Quotachecks happen at mount time only). */ if (XFS_IS_UQUOTA_ON(mp)) { ASSERT(udqp); xfs_qm_quotacheck_dqadjust(udqp, nblks, rtblks); xfs_qm_dqput(udqp); } if (XFS_IS_OQUOTA_ON(mp)) { ASSERT(gdqp); xfs_qm_quotacheck_dqadjust(gdqp, nblks, rtblks); xfs_qm_dqput(gdqp); } /* * Now release the inode. This will send it to 'inactive', and * possibly even free blocks. */ VN_RELE(XFS_ITOV(ip)); /* * Goto next inode. */ *res = BULKSTAT_RV_DIDONE; return (0);}/* * Walk thru all the filesystem inodes and construct a consistent view * of the disk quota world. If the quotacheck fails, disable quotas. */intxfs_qm_quotacheck( xfs_mount_t *mp){ int done, count, error; xfs_ino_t lastino; size_t structsz; xfs_inode_t *uip, *gip; uint flags; count = INT_MAX; structsz = 1; lastino = 0; flags = 0; ASSERT(XFS_QI_UQIP(mp) || XFS_QI_GQIP(mp)); ASSERT(XFS_IS_QUOTA_RUNNING(mp)); /* * There should be no cached dquots. The (simplistic) quotacheck * algorithm doesn't like that. */ ASSERT(XFS_QI_MPLNDQUOTS(mp) == 0); cmn_err(CE_NOTE, "XFS quotacheck %s: Please wait.", mp->m_fsname); /* * First we go thru all the dquots on disk, USR and GRP/PRJ, and reset * their counters to zero. We need a clean slate. * We don't log our changes till later. */ if ((uip = XFS_QI_UQIP(mp))) { if ((error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA))) goto error_return; flags |= XFS_UQUOTA_CHKD; } if ((gip = XFS_QI_GQIP(mp))) { if ((error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ? XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA))) goto error_return; flags |= XFS_OQUOTA_CHKD; } do { /* * Iterate thru all the inodes in the file system, * adjusting the corresponding dquot counters in core. */ if ((error = xfs_bulkstat(mp, &lastino, &count, xfs_qm_dqusage_adjust, NULL, structsz, NULL, BULKSTAT_FG_IGET|BULKSTAT_FG_VFSLOCKED, &done))) break; } while (! done); /* * We can get this error if we couldn't do a dquot allocation inside * xfs_qm_dqusage_adjust (via bulkstat). We don't care about the * dirty dquots that might be cached, we just want to get rid of them * and turn quotaoff. The dquots won't be attached to any of the inodes * at this point (because we intentionally didn't in dqget_noattach). */ if (error) { xfs_qm_dqpurge_all(mp, XFS_QMOPT_UQUOTA|XFS_QMOPT_GQUOTA| XFS_QMOPT_PQUOTA|XFS_QMOPT_QUOTAOFF); goto error_return; } /* * We've made all the changes that we need to make incore. * Now flush_them down to disk buffers. */ xfs_qm_dqflush_all(mp, XFS_QMOPT_DELWRI); /* * We didn't log anything, because if we crashed, we'll have to * start the quotacheck from scratch anyway. However, we must make * sure that our dquot changes are secure before we put the * quotacheck'd stamp on the superblock. So, here we do a synchronous * flush. */ XFS_bflush(mp->m_ddev_targp); /* * If one type of quotas is off, then it will lose its * quotachecked status, since we won't be doing accounting for * that type anymore. */ mp->m_qflags &= ~(XFS_OQUOTA_CHKD | XFS_UQUOTA_CHKD); mp->m_qflags |= flags; XQM_LIST_PRINT(&(XFS_QI_MPL_LIST(mp)), MPL_NEXT, "++++ Mp list +++"); error_return: if (error) { cmn_err(CE_WARN, "XFS quotacheck %s: Unsuccessful (Error %d): " "Disabling quotas.", mp->m_fsname, error); /* * We must turn off quotas. */ ASSERT(mp->m_quotainfo != NULL); ASSERT(xfs_Gqm != NULL); xfs_qm_destroy_quotainfo(mp); (void)xfs_mount_reset_sbqflags(mp); } else { cmn_err(CE_NOTE, "XFS quotacheck %s: Done.", mp->m_fsname); } return (error);}/* * This is called after the superblock has been read in and we're ready to * iget the quota inodes. */STATIC intxfs_qm_init_quotainos( xfs_mount_t *mp){ xfs_inode_t *uip, *gip; int error; __int64_t sbflags; uint flags; ASSERT(mp->m_quotainfo); uip = gip = NULL; sbflags = 0; flags = 0; /* * Get the uquota and gquota inodes */ if (XFS_SB_VERSION_HASQUOTA(&mp->m_sb)) { if (XFS_IS_UQUOTA_ON(mp) && mp->m_sb.sb_uquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_uquotino > 0); if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &uip, 0))) return XFS_ERROR(error); } if (XFS_IS_OQUOTA_ON(mp) && mp->m_sb.sb_gquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_gquotino > 0); if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &gip, 0))) { if (uip) VN_RELE(XFS_ITOV(uip)); return XFS_ERROR(error); } } } else { flags |= XFS_QMOPT_SBVERSION; sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | XFS_SB_QFLAGS); } /* * Create the two inodes, if they don't exist already. The changes * made above will get added to a transaction and logged in one of * the qino_alloc calls below. If the device is readonly, * temporarily switch to read-write to do this. */ if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) { if ((error = xfs_qm_qino_alloc(mp, &uip, sbflags | XFS_SB_UQUOTINO, flags | XFS_QMOPT_UQUOTA))) return XFS_ERROR(error); flags &= ~XFS_QMOPT_SBVERSION; } if (XFS_IS_OQUOTA_ON(mp) && gip == NULL) { flags |= (XFS_IS_GQUOTA_ON(mp) ? XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA); error = xfs_qm_qino_alloc(mp, &gip, sbflags | XFS_SB_GQUOTINO, flags); if (error) { if (uip) VN_RELE(XFS_ITOV(uip)); return XFS_ERROR(error); } } XFS_QI_UQIP(mp) = uip; XFS_QI_GQIP(mp) = gip; return (0);}/* * Traverse the freelist of dquots and attempt to reclaim a maximum of * 'howmany' dquots. This operation races with dqlookup(), and attempts to * favor the lookup function ... * XXXsup merge this with qm_reclaim_one(). */STATIC intxfs_qm_shake_freelist( int howmany){ int nreclaimed; xfs_dqhash_t *hash; xfs_dquot_t *dqp, *nextdqp; int restarts; int nflushes; if (howmany <= 0) return (0); nreclaimed = 0; restarts = 0; nflushes = 0;#ifdef QUOTADEBUG cmn_err(CE_DEBUG, "Shake free 0x%x", howmany);#endif /* lock order is : hashchainlock, freelistlock, mplistlock */ tryagain: xfs_qm_freelist_lock(xfs_Gqm);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -