Skip to content

Commit 07137e9

Browse files
author
Darrick J. Wong
committed
xfs: don't lose solo dquot update transactions
Quota counter updates are tracked via incore objects which hang off the xfs_trans object. These changes are then turned into dirty log items in xfs_trans_apply_dquot_deltas just prior to commiting the log items to the CIL. However, updating the incore deltas do not cause XFS_TRANS_DIRTY to be set on the transaction. In other words, a pure quota counter update will be silently discarded if there are no other dirty log items attached to the transaction. This is currently not the case anywhere in the filesystem because quota updates always dirty at least one other metadata item, but a subsequent bug fix will add dquot log item precommits, so we actually need a dirty dquot log item prior to xfs_trans_run_precommits. Also let's not leave a logic bomb. Cc: <stable@vger.kernel.org> # v2.6.35 Fixes: 0924378 ("xfs: split out iclog writing from xfs_trans_commit()") Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent 3762113 commit 07137e9

File tree

3 files changed

+32
-14
lines changed

3 files changed

+32
-14
lines changed

fs/xfs/xfs_quota.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ extern void xfs_trans_free_dqinfo(struct xfs_trans *);
101101
extern void xfs_trans_mod_dquot_byino(struct xfs_trans *, struct xfs_inode *,
102102
uint, int64_t);
103103
extern void xfs_trans_apply_dquot_deltas(struct xfs_trans *);
104-
extern void xfs_trans_unreserve_and_mod_dquots(struct xfs_trans *);
104+
void xfs_trans_unreserve_and_mod_dquots(struct xfs_trans *tp,
105+
bool already_locked);
105106
int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp, struct xfs_inode *ip,
106107
int64_t dblocks, int64_t rblocks, bool force);
107108
extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
@@ -173,7 +174,7 @@ static inline void xfs_trans_mod_dquot_byino(struct xfs_trans *tp,
173174
{
174175
}
175176
#define xfs_trans_apply_dquot_deltas(tp)
176-
#define xfs_trans_unreserve_and_mod_dquots(tp)
177+
#define xfs_trans_unreserve_and_mod_dquots(tp, a)
177178
static inline int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp,
178179
struct xfs_inode *ip, int64_t dblocks, int64_t rblocks,
179180
bool force)

fs/xfs/xfs_trans.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,7 @@ __xfs_trans_commit(
866866
*/
867867
if (tp->t_flags & XFS_TRANS_SB_DIRTY)
868868
xfs_trans_apply_sb_deltas(tp);
869+
xfs_trans_apply_dquot_deltas(tp);
869870

870871
error = xfs_trans_run_precommits(tp);
871872
if (error)
@@ -894,11 +895,6 @@ __xfs_trans_commit(
894895

895896
ASSERT(tp->t_ticket != NULL);
896897

897-
/*
898-
* If we need to update the superblock, then do it now.
899-
*/
900-
xfs_trans_apply_dquot_deltas(tp);
901-
902898
xlog_cil_commit(log, tp, &commit_seq, regrant);
903899

904900
xfs_trans_free(tp);
@@ -924,7 +920,7 @@ __xfs_trans_commit(
924920
* the dqinfo portion to be. All that means is that we have some
925921
* (non-persistent) quota reservations that need to be unreserved.
926922
*/
927-
xfs_trans_unreserve_and_mod_dquots(tp);
923+
xfs_trans_unreserve_and_mod_dquots(tp, true);
928924
if (tp->t_ticket) {
929925
if (regrant && !xlog_is_shutdown(log))
930926
xfs_log_ticket_regrant(log, tp->t_ticket);
@@ -1018,7 +1014,7 @@ xfs_trans_cancel(
10181014
}
10191015
#endif
10201016
xfs_trans_unreserve_and_mod_sb(tp);
1021-
xfs_trans_unreserve_and_mod_dquots(tp);
1017+
xfs_trans_unreserve_and_mod_dquots(tp, false);
10221018

10231019
if (tp->t_ticket) {
10241020
xfs_log_ticket_ungrant(log, tp->t_ticket);

fs/xfs/xfs_trans_dquot.c

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,24 @@ xfs_trans_apply_dquot_deltas(
606606
ASSERT(dqp->q_blk.reserved >= dqp->q_blk.count);
607607
ASSERT(dqp->q_ino.reserved >= dqp->q_ino.count);
608608
ASSERT(dqp->q_rtb.reserved >= dqp->q_rtb.count);
609+
610+
/*
611+
* We've applied the count changes and given back
612+
* whatever reservation we didn't use. Zero out the
613+
* dqtrx fields.
614+
*/
615+
qtrx->qt_blk_res = 0;
616+
qtrx->qt_bcount_delta = 0;
617+
qtrx->qt_delbcnt_delta = 0;
618+
619+
qtrx->qt_rtblk_res = 0;
620+
qtrx->qt_rtblk_res_used = 0;
621+
qtrx->qt_rtbcount_delta = 0;
622+
qtrx->qt_delrtb_delta = 0;
623+
624+
qtrx->qt_ino_res = 0;
625+
qtrx->qt_ino_res_used = 0;
626+
qtrx->qt_icount_delta = 0;
609627
}
610628
}
611629
}
@@ -642,7 +660,8 @@ xfs_trans_unreserve_and_mod_dquots_hook(
642660
*/
643661
void
644662
xfs_trans_unreserve_and_mod_dquots(
645-
struct xfs_trans *tp)
663+
struct xfs_trans *tp,
664+
bool already_locked)
646665
{
647666
int i, j;
648667
struct xfs_dquot *dqp;
@@ -671,10 +690,12 @@ xfs_trans_unreserve_and_mod_dquots(
671690
* about the number of blocks used field, or deltas.
672691
* Also we don't bother to zero the fields.
673692
*/
674-
locked = false;
693+
locked = already_locked;
675694
if (qtrx->qt_blk_res) {
676-
xfs_dqlock(dqp);
677-
locked = true;
695+
if (!locked) {
696+
xfs_dqlock(dqp);
697+
locked = true;
698+
}
678699
dqp->q_blk.reserved -=
679700
(xfs_qcnt_t)qtrx->qt_blk_res;
680701
}
@@ -695,7 +716,7 @@ xfs_trans_unreserve_and_mod_dquots(
695716
dqp->q_rtb.reserved -=
696717
(xfs_qcnt_t)qtrx->qt_rtblk_res;
697718
}
698-
if (locked)
719+
if (locked && !already_locked)
699720
xfs_dqunlock(dqp);
700721

701722
}

0 commit comments

Comments
 (0)