// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2002 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_trans.h"
#include "xfs_trans_priv.h"
#include "xfs_quota.h"
#include "xfs_qm.h"
#include "xfs_trace.h"
#include "xfs_error.h"
#include "xfs_health.h"
STATIC void xfs_trans_alloc_dqinfo(xfs_trans_t *);
/*
* Add the locked dquot to the transaction.
* The dquot must be locked, and it cannot be associated with any
* transaction.
*/
void
xfs_trans_dqjoin(
struct xfs_trans *tp,
struct xfs_dquot *dqp)
{
ASSERT(XFS_DQ_IS_LOCKED(dqp));
ASSERT(dqp->q_logitem.qli_dquot == dqp);
/*
* Get a log_item_desc to point at the new item.
*/
xfs_trans_add_item(tp, &dqp->q_logitem.qli_item);
}
/*
* This is called to mark the dquot as needing
* to be logged when the transaction is committed. The dquot must
* already be associated with the given transaction.
* Note that it marks the entire transaction as dirty. In the ordinary
* case, this gets called via xfs_trans_commit, after the transaction
* is already dirty. However, there's nothing stop this from getting
* called directly, as done by xfs_qm_scall_setqlim. Hence, the TRANS_DIRTY
* flag.
*/
void
xfs_trans_log_dquot(
struct xfs_trans *tp,
struct xfs_dquot *dqp)
{
ASSERT(XFS_DQ_IS_LOCKED(dqp));
/* Upgrade the dquot to bigtime format if possible. */
if (dqp->q_id != 0 &&
xfs_has_bigtime(tp->t_mountp) &&
!(dqp->q_type & XFS_DQTYPE_BIGTIME))
dqp->q_type |= XFS_DQTYPE_BIGTIME;
tp->t_flags |= XFS_TRANS_DIRTY;
set_bit(XFS_LI_DIRTY, &dqp->q_logitem.qli_item.li_flags);
}
/*
* Carry forward whatever is left of the quota blk reservation to
* the spanky new transaction
*/
void
xfs_trans_dup_dqinfo(
struct xfs_trans *otp,
struct xfs_trans *ntp)
{
struct xfs_dqtrx *oq, *nq;
int i, j;
struct xfs_dqtrx *oqa, *nqa;
uint64_t blk_res_used;
if (!otp->t_dqinfo)
return;
xfs_trans_alloc_dqinfo(ntp);
for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) {
oqa = otp->t_dqinfo->dqs[j];
nqa = ntp->t_dqinfo->dqs[j];
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
blk_res_used = 0;
if (oqa[i].qt_dquot == NULL)
break;
oq = &oqa[i];
nq = &nqa[i];
if (oq->qt_blk_res && oq->qt_bcount_delta > 0)
blk_res_used = oq->qt_bcount_delta;
nq->qt_dquot = oq->qt_dquot;
nq->qt_bcount_delta = nq->qt_icount_delta = 0;
nq->qt_rtbcount_delta = 0;
/*
* Transfer whatever is left of the reservations.
*/
nq->qt_blk_res = oq->qt_blk_res - blk_res_used;
oq->qt_blk_res = blk_res_used;
nq->qt_rtblk_res = oq->qt_rtblk_res -
oq->qt_rtblk_res_used;
oq->qt_rtblk_res = oq->qt_rtblk_res_used;
nq->qt_ino_res = oq->qt_ino_res - oq->qt_ino_res_used;
oq->qt_ino_res = oq->qt_ino_res_used;
}
}
}
#ifdef CONFIG_XFS_LIVE_HOOKS
/*
* Use a static key here to reduce the overhead of quota live updates. If the
* compiler supports jump labels, the static branch will be replaced by a nop
* sled when there are no hook users. Online fsck is currently the only
* caller, so this is a reasonable tradeoff.
*
* Note: Patching the kernel code requires taking the cpu hotplug lock. Other
* parts of the kernel allocate memory with that lock held, which means that
* XFS callers cannot hold any locks that might be used by memory reclaim or
* writeback when calling the static_branch_{inc,dec} functions.
*/
DEFINE_STATIC_XFS_HOOK_SWITCH(xfs_dqtrx_hooks_switch);
void
xfs_dqtrx_hook_disable(void)
{
xfs_hooks_switch_off(&xfs_dqtrx_hooks_switch);
}
void
xfs_dqtrx_hook_enable(void)
{
xfs_hooks_switch_on(&xfs_dqtrx_hooks_switch);
}
/* Schedule a transactional dquot update on behalf of an inode. */
void
xfs_trans_mod_ino_dquot(
struct xfs_trans *tp,
struct xfs_inode *ip,
struct xfs_dquot *dqp,
unsigned int field,
int64_t delta)
{
if (xfs_is_metadir_inode(ip))
return;
xfs_trans_mod_dquot(tp, dqp, field, delta);
if (xfs_hooks_switched_on(&xfs_dqtrx_hooks_switch)) {
struct xfs_mod_ino_dqtrx_params p = {
.tx_id = (uintptr_t)tp,
.ino = ip->i_ino,
.q_type = xfs_dquot_type(dqp),
.q_id = dqp->q_id,
.delta = delta
};
struct xfs_quotainfo *qi = tp->t_mountp->m_quotainfo;
xfs_hooks_call(&qi->qi_mod_ino_dqtrx_hooks, field, &p);
}
}
/* Call the specified functions during a dquot counter update. */
int
xfs_dqtrx_hook_add(
struct xfs_quotainfo *qi,
struct xfs_dqtrx_hook *hook)
{
int error;
/*
* Transactional dquot updates first call the mod hook when changes
* are attached to the transaction and then call the apply hook when
* those changes are committed (or canceled).
*
* The apply hook must be installed before the mod hook so that we
* never fail to catch the end of a quota update sequence.
*/
error = xfs_hooks_add(&qi->qi_apply_dqtrx_hooks, &hook->apply_hook);
if (error)
goto out;
error = xfs_hooks_add(&qi->qi_mod_ino_dqtrx_hooks, &hook->mod_hook);
if (error