/* * Lock order: * * ip->i_lock * qi->qi_tree_lock * dquot->q_qlock (xfs_dqlock() and friends) * dquot->q_flush (xfs_dqflock() and friends) * qi->qi_lru_lock * * If two dquots need to be locked the order is user before group/project, * otherwise by the lowest id first, see xfs_dqlock2.
*/
/* Record observations of quota corruption with the health tracking system. */ staticvoid
xfs_dquot_mark_sick( struct xfs_dquot *dqp)
{ struct xfs_mount *mp = dqp->q_mount;
switch (dqp->q_type) { case XFS_DQTYPE_USER:
xfs_fs_mark_sick(mp, XFS_SICK_FS_UQUOTA); break; case XFS_DQTYPE_GROUP:
xfs_fs_mark_sick(mp, XFS_SICK_FS_GQUOTA); break; case XFS_DQTYPE_PROJ:
xfs_fs_mark_sick(mp, XFS_SICK_FS_PQUOTA); break; default:
ASSERT(0); break;
}
}
/* * Detach the dquot buffer if it's still attached, because we can get called * through dqpurge after a log shutdown. Caller must hold the dqflock or have * otherwise isolated the dquot.
*/ void
xfs_dquot_detach_buf( struct xfs_dquot *dqp)
{ struct xfs_dq_logitem *qlip = &dqp->q_logitem; struct xfs_buf *bp = NULL;
spin_lock(&qlip->qli_lock); if (qlip->qli_item.li_buf) {
bp = qlip->qli_item.li_buf;
qlip->qli_item.li_buf = NULL;
}
spin_unlock(&qlip->qli_lock); if (bp) {
xfs_buf_lock(bp);
list_del_init(&qlip->qli_item.li_bio_list);
xfs_buf_relse(bp);
}
}
/* * This is called to free all the memory associated with a dquot
*/ void
xfs_qm_dqdestroy( struct xfs_dquot *dqp)
{
ASSERT(list_empty(&dqp->q_lru));
ASSERT(dqp->q_logitem.qli_item.li_buf == NULL);
/* * 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.
*/ void
xfs_qm_adjust_dqlimits( struct xfs_dquot *dq)
{ struct xfs_mount *mp = dq->q_mount; struct xfs_quotainfo *q = mp->m_quotainfo; struct xfs_def_quota *defq; int prealloc = 0;
if (!dq->q_blk.softlimit) {
dq->q_blk.softlimit = defq->blk.soft;
prealloc = 1;
} if (!dq->q_blk.hardlimit) {
dq->q_blk.hardlimit = defq->blk.hard;
prealloc = 1;
} if (!dq->q_ino.softlimit)
dq->q_ino.softlimit = defq->ino.soft; if (!dq->q_ino.hardlimit)
dq->q_ino.hardlimit = defq->ino.hard; if (!dq->q_rtb.softlimit)
dq->q_rtb.softlimit = defq->rtb.soft; if (!dq->q_rtb.hardlimit)
dq->q_rtb.hardlimit = defq->rtb.hard;
if (prealloc)
xfs_dquot_set_prealloc_limits(dq);
}
/* Set the expiration time of a quota's grace period. */
time64_t
xfs_dquot_set_timeout( struct xfs_mount *mp,
time64_t timeout)
{ struct xfs_quotainfo *qi = mp->m_quotainfo;
/* Set the length of the default grace period. */
time64_t
xfs_dquot_set_grace_period(
time64_t grace)
{ return clamp_t(time64_t, grace, XFS_DQ_GRACE_MIN, XFS_DQ_GRACE_MAX);
}
/* * Determine if this quota counter is over either limit and set the quota * timers as appropriate.
*/ staticinlinevoid
xfs_qm_adjust_res_timer( struct xfs_mount *mp, struct xfs_dquot_res *res, struct xfs_quota_limits *qlim)
{
ASSERT(res->hardlimit == 0 || res->softlimit <= res->hardlimit);
/* * 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).
*/ void
xfs_qm_adjust_dqtimers( struct xfs_dquot *dq)
{ struct xfs_mount *mp = dq->q_mount; struct xfs_quotainfo *qi = mp->m_quotainfo; struct xfs_def_quota *defq;
/* * ID of the first dquot in the block - id's are zero based.
*/
curid = id - (id % q->qi_dqperchunk);
memset(d, 0, BBTOB(q->qi_dqchunklen)); for (i = 0; i < q->qi_dqperchunk; i++, d++, curid++) {
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(curid);
d->dd_diskdq.d_type = type; if (curid > 0 && xfs_has_bigtime(mp))
d->dd_diskdq.d_type |= XFS_DQTYPE_BIGTIME; if (xfs_has_crc(mp)) {
uuid_copy(&d->dd_uuid, &mp->m_sb.sb_meta_uuid);
xfs_update_cksum((char *)d, sizeof(struct xfs_dqblk),
XFS_DQUOT_CRC_OFF);
}
}
xfs_trans_dquot_buf(tp, bp, blftype);
/* * quotacheck uses delayed writes to update all the dquots on disk in an * efficient manner instead of logging the individual dquot changes as * they are made. However if we log the buffer allocated here and crash * after quotacheck while the logged initialisation is still in the * active region of the log, log recovery can replay the dquot buffer * initialisation over the top of the checked dquots and corrupt quota * accounting. * * To avoid this problem, quotacheck cannot log the initialised buffer. * We must still dirty the buffer and write it back before the * allocation transaction clears the log. Therefore, mark the buffer as * ordered instead of logging it directly. This is safe for quotacheck * because it detects and repairs allocated but initialized dquot blocks * in the quota inodes.
*/ if (!(mp->m_qflags & qflag))
xfs_trans_ordered_buf(tp, bp); else
xfs_trans_log_buf(tp, bp, 0, BBTOB(q->qi_dqchunklen) - 1);
}
space = div_u64(pre->q_prealloc_hi_wmark, 100); if (!pre->q_prealloc_lo_wmark)
pre->q_prealloc_lo_wmark = space * 95;
pre->q_low_space[XFS_QLOWSP_1_PCNT] = space;
pre->q_low_space[XFS_QLOWSP_3_PCNT] = space * 3;
pre->q_low_space[XFS_QLOWSP_5_PCNT] = space * 5;
}
/* * Initialize the dynamic speculative preallocation thresholds. The lo/hi * watermarks correspond to the soft and hard limits by default. If a soft limit * is not specified, we use 95% of the hard limit.
*/ void
xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp)
{
xfs_dquot_set_prealloc(&dqp->q_blk_prealloc, &dqp->q_blk);
xfs_dquot_set_prealloc(&dqp->q_rtb_prealloc, &dqp->q_rtb);
}
/* * Ensure that the given in-core dquot has a buffer on disk backing it, and * return the buffer locked and held. This is called when the bmapi finds a * hole.
*/ STATICint
xfs_dquot_disk_alloc( struct xfs_dquot *dqp, struct xfs_buf **bpp)
{ struct xfs_bmbt_irec map; struct xfs_trans *tp; struct xfs_mount *mp = dqp->q_mount; struct xfs_buf *bp;
xfs_dqtype_t qtype = xfs_dquot_type(dqp); struct xfs_inode *quotip = xfs_quota_inode(mp, qtype); int nmaps = 1; int error;
if (!xfs_this_quota_on(dqp->q_mount, qtype)) { /* * Return if this type of quotas is turned off while we didn't * have an inode lock
*/
error = -ESRCH; goto err_cancel;
}
error = xfs_iext_count_extend(tp, quotip, XFS_DATA_FORK,
XFS_IEXT_ADD_NOSPLIT_CNT); if (error) goto err_cancel;
/* Create the block mapping. */
error = xfs_bmapi_write(tp, quotip, dqp->q_fileoffset,
XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA, 0, &map,
&nmaps); if (error) goto err_cancel;
/* * 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) */
error = xfs_trans_get_buf(tp, mp->m_ddev_targp, dqp->q_blkno,
mp->m_quotainfo->qi_dqchunklen, 0, &bp); if (error) goto err_cancel;
bp->b_ops = &xfs_dquot_buf_ops;
/* * Make a chunk of dquots out of this buffer and log * the entire thing.
*/
xfs_qm_init_dquot_blk(tp, dqp->q_id, qtype, bp);
xfs_buf_set_ref(bp, XFS_DQUOT_REF);
/* * Hold the buffer and join it to the dfops so that we'll still own * the buffer when we return to the caller. The buffer disposal on * error must be paid attention to very carefully, as it has been * broken since commit efa092f3d4c6 "[XFS] Fixes a bug in the quota * code when allocating a new dquot record" in 2005, and the later * conversion to xfs_defer_ops in commit 310a75a3c6c747 failed to keep * the buffer locked across the _defer_finish call. We can now do * this correctly with xfs_defer_bjoin. * * Above, we allocated a disk block for the dquot information and used * get_buf to initialize the dquot. If the _defer_finish fails, the old * transaction is gone but the new buffer is not joined or held to any * transaction, so we must _buf_relse it. * * If everything succeeds, the caller of this function is returned a * buffer that is locked and held to the transaction. The caller * is responsible for unlocking any buffer passed back, either * manually or by committing the transaction. On error, the buffer is * released and not passed back. * * Keep the quota inode ILOCKed until after the transaction commit to * maintain the atomicity of bmap/rmap updates.
*/
xfs_trans_bhold(tp, bp);
error = xfs_trans_commit(tp);
xfs_iunlock(quotip, XFS_ILOCK_EXCL); if (error) {
xfs_buf_relse(bp); return error;
}
/* * Read in the in-core dquot's on-disk metadata and return the buffer. * Returns ENOENT to signal a hole.
*/ STATICint
xfs_dquot_disk_read( struct xfs_mount *mp, struct xfs_dquot *dqp, struct xfs_buf **bpp)
{ struct xfs_bmbt_irec map; struct xfs_buf *bp;
xfs_dqtype_t qtype = xfs_dquot_type(dqp); struct xfs_inode *quotip = xfs_quota_inode(mp, qtype);
uint lock_mode; int nmaps = 1; int error;
lock_mode = xfs_ilock_data_map_shared(quotip); if (!xfs_this_quota_on(mp, qtype)) { /* * Return if this type of quotas is turned off while we * didn't have the quota inode lock.
*/
xfs_iunlock(quotip, lock_mode); return -ESRCH;
}
/* * Find the block map; no allocations yet
*/
error = xfs_bmapi_read(quotip, dqp->q_fileoffset,
XFS_DQUOT_CLUSTER_SIZE_FSB, &map, &nmaps, 0);
xfs_iunlock(quotip, lock_mode); if (error) return error;
dqp->q_type = type;
dqp->q_id = id;
dqp->q_mount = mp;
INIT_LIST_HEAD(&dqp->q_lru);
mutex_init(&dqp->q_qlock);
init_waitqueue_head(&dqp->q_pinwait);
dqp->q_fileoffset = (xfs_fileoff_t)id / mp->m_quotainfo->qi_dqperchunk; /* * Offset of dquot in the (fixed sized) dquot chunk.
*/
dqp->q_bufoffset = (id % mp->m_quotainfo->qi_dqperchunk) * sizeof(struct xfs_dqblk);
/* * Because we want to use a counting completion, complete * the flush completion once to allow a single access to * the flush completion without blocking.
*/
init_completion(&dqp->q_flush);
complete(&dqp->q_flush);
/* * Make sure group quotas have a different lock class than user * quotas.
*/ switch (type) { case XFS_DQTYPE_USER: /* uses the default lock class */ break; case XFS_DQTYPE_GROUP:
lockdep_set_class(&dqp->q_qlock, &xfs_dquot_group_class); break; case XFS_DQTYPE_PROJ:
lockdep_set_class(&dqp->q_qlock, &xfs_dquot_project_class); break; default:
ASSERT(0); break;
}
xfs_qm_dquot_logitem_init(dqp);
XFS_STATS_INC(mp, xs_qm_dquot); return dqp;
}
/* Check the ondisk dquot's id and type match what the incore dquot expects. */ staticbool
xfs_dquot_check_type( struct xfs_dquot *dqp, struct xfs_disk_dquot *ddqp)
{
uint8_t ddqp_type;
uint8_t dqp_type;
if (be32_to_cpu(ddqp->d_id) != dqp->q_id) returnfalse;
/* * V5 filesystems always expect an exact type match. V4 filesystems * expect an exact match for user dquots and for non-root group and * project dquots.
*/ if (xfs_has_crc(dqp->q_mount) ||
dqp_type == XFS_DQTYPE_USER || dqp->q_id != 0) return ddqp_type == dqp_type;
/* * V4 filesystems support either group or project quotas, but not both * at the same time. The non-user quota file can be switched between * group and project quota uses depending on the mount options, which * means that we can encounter the other type when we try to load quota * defaults. Quotacheck will soon reset the entire quota file * (including the root dquot) anyway, but don't log scary corruption * reports to dmesg.
*/ return ddqp_type == XFS_DQTYPE_GROUP || ddqp_type == XFS_DQTYPE_PROJ;
}
/* Copy the in-core quota fields in from the on-disk buffer. */ STATICint
xfs_dquot_from_disk( struct xfs_dquot *dqp, struct xfs_buf *bp)
{ struct xfs_dqblk *dqb = xfs_buf_offset(bp, dqp->q_bufoffset); struct xfs_disk_dquot *ddqp = &dqb->dd_diskdq;
/* * Ensure that we got the type and ID we were looking for. * Everything else was checked by the dquot buffer verifier.
*/ if (!xfs_dquot_check_type(dqp, ddqp)) {
xfs_alert_tag(bp->b_mount, XFS_PTAG_VERIFIER_ERROR, "Metadata corruption detected at %pS, quota %u",
__this_address, dqp->q_id);
xfs_alert(bp->b_mount, "Unmount and run xfs_repair");
xfs_dquot_mark_sick(dqp); return -EFSCORRUPTED;
}
/* copy everything from disk dquot to the incore dquot */
dqp->q_type = ddqp->d_type;
dqp->q_blk.hardlimit = be64_to_cpu(ddqp->d_blk_hardlimit);
dqp->q_blk.softlimit = be64_to_cpu(ddqp->d_blk_softlimit);
dqp->q_ino.hardlimit = be64_to_cpu(ddqp->d_ino_hardlimit);
dqp->q_ino.softlimit = be64_to_cpu(ddqp->d_ino_softlimit);
dqp->q_rtb.hardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit);
dqp->q_rtb.softlimit = be64_to_cpu(ddqp->d_rtb_softlimit);
/* * Reservation counters are defined as reservation plus current usage * to avoid having to add every time.
*/
dqp->q_blk.reserved = dqp->q_blk.count;
dqp->q_ino.reserved = dqp->q_ino.count;
dqp->q_rtb.reserved = dqp->q_rtb.count;
/* * Read in the ondisk dquot using dqtobp() then copy it to an incore version, * and release the buffer immediately. If @can_alloc is true, fill any * holes in the on-disk metadata.
*/ staticint
xfs_qm_dqread( struct xfs_mount *mp,
xfs_dqid_t id,
xfs_dqtype_t type, bool can_alloc, struct xfs_dquot **dqpp)
{ struct xfs_dquot *dqp; struct xfs_buf *bp; int error;
/* Try to read the buffer, allocating if necessary. */
error = xfs_dquot_disk_read(mp, dqp, &bp); if (error == -ENOENT && can_alloc)
error = xfs_dquot_disk_alloc(dqp, &bp); if (error) goto err;
/* * At this point we should have a clean locked buffer. Copy the data * to the incore dquot and release the buffer since the incore dquot * has its own locking protocol so we needn't tie up the buffer any * further.
*/
ASSERT(xfs_buf_islocked(bp));
error = xfs_dquot_from_disk(dqp, bp);
xfs_buf_relse(bp); if (error) goto err;
/* * Advance to the next id in the current chunk, or if at the * end of the chunk, skip ahead to first id in next allocated chunk * using the SEEK_DATA interface.
*/ staticint
xfs_dq_get_next_id( struct xfs_mount *mp,
xfs_dqtype_t type,
xfs_dqid_t *id)
{ struct xfs_inode *quotip = xfs_quota_inode(mp, type);
xfs_dqid_t next_id = *id + 1; /* simple advance */
uint lock_flags; struct xfs_bmbt_irec got; struct xfs_iext_cursor cur;
xfs_fsblock_t start; int error = 0;
/* If we'd wrap past the max ID, stop */ if (next_id < *id) return -ENOENT;
/* If new ID is within the current chunk, advancing it sufficed */ if (next_id % mp->m_quotainfo->qi_dqperchunk) {
*id = next_id; return 0;
}
/* Nope, next_id is now past the current chunk, so find the next one */
start = (xfs_fsblock_t)next_id / mp->m_quotainfo->qi_dqperchunk;
if (xfs_iext_lookup_extent(quotip, "ip->i_df, start, &cur, &got)) { /* contiguous chunk, bump startoff for the id calculation */ if (got.br_startoff < start)
got.br_startoff = start;
*id = got.br_startoff * mp->m_quotainfo->qi_dqperchunk;
} else {
error = -ENOENT;
}
xfs_iunlock(quotip, lock_flags);
return error;
}
/* * Look up the dquot in the in-core cache. If found, the dquot is returned * locked and ready to go.
*/ staticstruct xfs_dquot *
xfs_qm_dqget_cache_lookup( struct xfs_mount *mp, struct xfs_quotainfo *qi, struct radix_tree_root *tree,
xfs_dqid_t id)
{ struct xfs_dquot *dqp;
/* * Try to insert a new dquot into the in-core cache. If an error occurs the * caller should throw away the dquot and start over. Otherwise, the dquot * is returned locked (and held by the cache) as if there had been a cache * hit. * * The insert needs to be done under memalloc_nofs context because the radix * tree can do memory allocation during insert. The qi->qi_tree_lock is taken in * memory reclaim when freeing unused dquots, so we cannot have the radix tree * node allocation recursing into filesystem reclaim whilst we hold the * qi_tree_lock.
*/ staticint
xfs_qm_dqget_cache_insert( struct xfs_mount *mp, struct xfs_quotainfo *qi, struct radix_tree_root *tree,
xfs_dqid_t id, struct xfs_dquot *dqp)
{ unsignedint nofs_flags; int error;
/* * Given a dquot id and type, read and initialize a dquot from the on-disk * metadata. This function is only for use during quota initialization so * it ignores the dquot cache assuming that the dquot shrinker isn't set up. * The caller is responsible for _qm_dqdestroy'ing the returned dquot.
*/ int
xfs_qm_dqget_uncached( struct xfs_mount *mp,
xfs_dqid_t id,
xfs_dqtype_t type, struct xfs_dquot **dqpp)
{ int error;
error = xfs_qm_dqget_checks(mp, type); if (error) return error;
return xfs_qm_dqread(mp, id, type, 0, dqpp);
}
/* Return the quota id for a given inode and type. */
xfs_dqid_t
xfs_qm_id_for_quotatype( struct xfs_inode *ip,
xfs_dqtype_t type)
{ switch (type) { case XFS_DQTYPE_USER: return i_uid_read(VFS_I(ip)); case XFS_DQTYPE_GROUP: return i_gid_read(VFS_I(ip)); case XFS_DQTYPE_PROJ: return ip->i_projid;
}
ASSERT(0); return 0;
}
/* * Return the dquot for a given inode and type. If @can_alloc is true, then * allocate blocks if needed. The inode's ILOCK must be held and it must not * have already had an inode attached.
*/ int
xfs_qm_dqget_inode( struct xfs_inode *ip,
xfs_dqtype_t type, bool can_alloc, struct xfs_dquot **O_dqpp)
{ struct xfs_mount *mp = ip->i_mount; struct xfs_quotainfo *qi = mp->m_quotainfo; struct radix_tree_root *tree = xfs_dquot_tree(qi, type); struct xfs_dquot *dqp;
xfs_dqid_t id; int error;
error = xfs_qm_dqget_checks(mp, type); if (error) return error;
/* * Dquot cache miss. We don't want to keep the inode lock across * a (potential) disk read. Also we don't want to deal with the lock * ordering between quotainode and this inode. OTOH, dropping the inode * lock here means dealing with a chown that can happen before * we re-acquire the lock.
*/
xfs_iunlock(ip, XFS_ILOCK_EXCL);
error = xfs_qm_dqread(mp, id, type, can_alloc, &dqp);
xfs_ilock(ip, XFS_ILOCK_EXCL); if (error) return error;
/* * A dquot could be attached to this inode by now, since we had * dropped the ilock.
*/ if (xfs_this_quota_on(mp, type)) { struct xfs_dquot *dqp1;
if (!XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
*dqpp = dqp; return 0;
}
xfs_qm_dqput(dqp);
}
return error;
}
/* * Release a reference to the dquot (decrement ref-count) and unlock it. * * If there is a group quota attached to this dquot, carefully release that * too without tripping over deadlocks'n'stuff.
*/ void
xfs_qm_dqput( struct xfs_dquot *dqp)
{
ASSERT(dqp->q_nrefs > 0);
ASSERT(XFS_DQ_IS_LOCKED(dqp));
if (list_lru_add_obj(&qi->qi_lru, &dqp->q_lru))
XFS_STATS_INC(dqp->q_mount, xs_qm_dquot_unused);
}
xfs_dqunlock(dqp);
}
/* * Release a dquot. Flush it if dirty, then dqput() it. * dquot must not be locked.
*/ void
xfs_qm_dqrele( struct xfs_dquot *dqp)
{ if (!dqp) return;
trace_xfs_dqrele(dqp);
xfs_dqlock(dqp); /* * We don't care to flush it if the dquot is dirty here. * That will create stutters that we want to avoid. * Instead we do a delayed write when we try to reclaim * a dirty dquot. Also xfs_sync will take part of the burden...
*/
xfs_qm_dqput(dqp);
}
/* * This is the dquot flushing I/O completion routine. It is called * from interrupt level when the buffer containing the dquot is * flushed to disk. It is responsible for removing the dquot logitem * from the AIL if it has not been re-logged, and unlocking the dquot's * flush lock. This behavior is very similar to that of inodes..
*/ staticvoid
xfs_qm_dqflush_done( struct xfs_log_item *lip)
{ struct xfs_dq_logitem *qlip =
container_of(lip, struct xfs_dq_logitem, qli_item); struct xfs_dquot *dqp = qlip->qli_dquot; struct xfs_ail *ailp = lip->li_ailp; struct xfs_buf *bp = NULL;
xfs_lsn_t tail_lsn;
/* * We only want to pull the item from the AIL if its * location in the log has not changed since we started the flush. * Thus, we only bother if the dquot's lsn has * not changed. First we check the lsn outside the lock * since it's cheaper, and then we recheck while * holding the lock before removing the dquot from the AIL.
*/ if (test_bit(XFS_LI_IN_AIL, &lip->li_flags) &&
(lip->li_lsn == qlip->qli_flush_lsn ||
test_bit(XFS_LI_FAILED, &lip->li_flags))) {
spin_lock(&ailp->ail_lock);
clear_bit(XFS_LI_FAILED, &lip->li_flags); if (lip->li_lsn == qlip->qli_flush_lsn) { /* xfs_ail_update_finish() drops the AIL lock */
tail_lsn = xfs_ail_delete_one(ailp, lip);
xfs_ail_update_finish(ailp, tail_lsn);
} else {
spin_unlock(&ailp->ail_lock);
}
}
/* * If this dquot hasn't been dirtied since initiating the last dqflush, * release the buffer reference. We already unlinked this dquot item * from the buffer.
*/
spin_lock(&qlip->qli_lock); if (!qlip->qli_dirty) {
bp = lip->li_buf;
lip->li_buf = NULL;
}
spin_unlock(&qlip->qli_lock); if (bp)
xfs_buf_rele(bp);
/* * Release the dq's flush lock since we're done with it.
*/
xfs_dqfunlock(dqp);
}
list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) {
list_del_init(&lip->li_bio_list);
xfs_qm_dqflush_done(lip);
}
}
/* Check incore dquot for errors before we flush. */ static xfs_failaddr_t
xfs_qm_dqflush_check( struct xfs_dquot *dqp)
{
xfs_dqtype_t type = xfs_dquot_type(dqp);
if (type != XFS_DQTYPE_USER &&
type != XFS_DQTYPE_GROUP &&
type != XFS_DQTYPE_PROJ) return __this_address;
if (dqp->q_id == 0) return NULL;
if (dqp->q_blk.softlimit && dqp->q_blk.count > dqp->q_blk.softlimit &&
!dqp->q_blk.timer) return __this_address;
if (dqp->q_ino.softlimit && dqp->q_ino.count > dqp->q_ino.softlimit &&
!dqp->q_ino.timer) return __this_address;
if (dqp->q_rtb.softlimit && dqp->q_rtb.count > dqp->q_rtb.softlimit &&
!dqp->q_rtb.timer) return __this_address;
/* bigtime flag should never be set on root dquots */ if (dqp->q_type & XFS_DQTYPE_BIGTIME) { if (!xfs_has_bigtime(dqp->q_mount)) return __this_address; if (dqp->q_id == 0) return __this_address;
}
return NULL;
}
/* * Get the buffer containing the on-disk dquot. * * Requires dquot flush lock, will clear the dirty flag, delete the quota log * item from the AIL, and shut down the system if something goes wrong.
*/ staticint
xfs_dquot_read_buf( struct xfs_trans *tp, struct xfs_dquot *dqp, struct xfs_buf **bpp)
{ struct xfs_mount *mp = dqp->q_mount; struct xfs_buf *bp = NULL; int error;
error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, dqp->q_blkno,
mp->m_quotainfo->qi_dqchunklen, 0,
&bp, &xfs_dquot_buf_ops); if (xfs_metadata_is_sick(error))
xfs_dquot_mark_sick(dqp); if (error) goto out_abort;
/* * Attach a dquot buffer to this dquot to avoid allocating a buffer during a * dqflush, since dqflush can be called from reclaim context. Caller must hold * the dqlock.
*/ int
xfs_dquot_attach_buf( struct xfs_trans *tp, struct xfs_dquot *dqp)
{ struct xfs_dq_logitem *qlip = &dqp->q_logitem; struct xfs_log_item *lip = &qlip->qli_item; int error;
spin_lock(&qlip->qli_lock); if (!lip->li_buf) { struct xfs_buf *bp = NULL;
spin_unlock(&qlip->qli_lock);
error = xfs_dquot_read_buf(tp, dqp, &bp); if (error) return error;
/* * Hold the dquot buffer so that we retain our ref to it after * detaching it from the transaction, then give that ref to the * dquot log item so that the AIL does not have to read the * dquot buffer to push this item.
*/
xfs_buf_hold(bp);
xfs_trans_brelse(tp, bp);
/* * Get a new reference the dquot buffer attached to this dquot for a dqflush * operation. * * Returns 0 and a NULL bp if none was attached to the dquot; 0 and a locked * bp; or -EAGAIN if the buffer could not be locked.
*/ int
xfs_dquot_use_attached_buf( struct xfs_dquot *dqp, struct xfs_buf **bpp)
{ struct xfs_buf *bp = dqp->q_logitem.qli_item.li_buf;
/* * A NULL buffer can happen if the dquot dirty flag was set but the * filesystem shut down before transaction commit happened. In that * case we're not going to flush anyway.
*/ if (!bp) {
ASSERT(xfs_is_shutdown(dqp->q_mount));
*bpp = NULL; return 0;
}
if (!xfs_buf_trylock(bp)) return -EAGAIN;
xfs_buf_hold(bp);
*bpp = bp; return 0;
}
/* * Write a modified dquot to disk. * The dquot must be locked and the flush lock too taken by caller. * The flush lock will not be unlocked until the dquot reaches the disk, * but the dquot is free to be unlocked and modified by the caller * in the interim. Dquot is still locked on return. This behavior is * identical to that of inodes.
*/ int
xfs_qm_dqflush( struct xfs_dquot *dqp, struct xfs_buf *bp)
{ struct xfs_mount *mp = dqp->q_mount; struct xfs_dq_logitem *qlip = &dqp->q_logitem; struct xfs_log_item *lip = &qlip->qli_item; struct xfs_dqblk *dqblk;
xfs_failaddr_t fa; int error;
trace_xfs_dqflush(dqp);
fa = xfs_qm_dqflush_check(dqp); if (fa) {
xfs_alert(mp, "corrupt dquot ID 0x%x in memory at %pS",
dqp->q_id, fa);
xfs_dquot_mark_sick(dqp);
error = -EFSCORRUPTED; goto out_abort;
}
/* Flush the incore dquot to the ondisk buffer. */
dqblk = xfs_buf_offset(bp, dqp->q_bufoffset);
xfs_dquot_to_disk(&dqblk->dd_diskdq, dqp);
/* * Clear the dirty field and remember the flush lsn for later use.
*/
dqp->q_flags &= ~XFS_DQFLAG_DIRTY;
/* * We hold the dquot lock, so nobody can dirty it while we're * scheduling the write out. Clear the dirty-since-flush flag.
*/
spin_lock(&qlip->qli_lock);
qlip->qli_dirty = false;
spin_unlock(&qlip->qli_lock);
/* * copy the lsn into the on-disk dquot now while we have the in memory * dquot here. This can't be done later in the write verifier as we * can't get access to the log item at that point in time. * * We also calculate the CRC here so that the on-disk dquot in the * buffer always has a valid CRC. This ensures there is no possibility * of a dquot without an up-to-date CRC getting to disk.
*/ if (xfs_has_crc(mp)) {
dqblk->dd_lsn = cpu_to_be64(lip->li_lsn);
xfs_update_cksum((char *)dqblk, sizeof(struct xfs_dqblk),
XFS_DQUOT_CRC_OFF);
}
/* * Attach the dquot to the buffer so that we can remove this dquot from * the AIL and release the flush lock once the dquot is synced to disk.
*/
bp->b_iodone = xfs_buf_dquot_iodone;
list_add_tail(&lip->li_bio_list, &bp->b_li_list);
/* * If the buffer is pinned then push on the log so we won't * get stuck waiting in the write for too long.
*/ if (xfs_buf_ispinned(bp)) {
trace_xfs_dqflush_force(dqp);
xfs_log_force(mp, 0);
}
/* Sort in order of dquot id, do not allow duplicates */ for (i = 0; i < XFS_QM_TRANS_MAXDQS && q[i].qt_dquot != NULL; i++) { unsignedint j;
for (j = 0; j < i; j++)
ASSERT(q[i].qt_dquot != q[j].qt_dquot);
} if (i == 0) return;
sort(q, i, sizeof(struct xfs_dqtrx), xfs_dqtrx_cmp, NULL);
mutex_lock(&q[0].qt_dquot->q_qlock); for (i = 1; i < XFS_QM_TRANS_MAXDQS && q[i].qt_dquot != NULL; i++)
mutex_lock_nested(&q[i].qt_dquot->q_qlock,
XFS_QLOCK_NESTED + i - 1);
}
int __init
xfs_qm_init(void)
{
xfs_dquot_cache = kmem_cache_create("xfs_dquot", sizeof(struct xfs_dquot),
0, 0, NULL); if (!xfs_dquot_cache) goto out;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.