Skip to content

Commit eae44cb

Browse files
author
Darrick J. Wong
committed
xfs: hold quota inode ILOCK_EXCL until the end of dqalloc
Online fsck depends on callers holding ILOCK_EXCL from the time they decide to update a block mapping until after they've updated the reverse mapping records to guarantee the stability of both mapping records. Unfortunately, the quota code drops ILOCK_EXCL at the first transaction roll in the dquot allocation process, which breaks that assertion. This leads to sporadic failures in the online rmap repair code if the repair code grabs the AGF after bmapi_write maps a new block into the quota file's data fork but before it can finish the deferred rmap update. Fix this by rewriting the function to hold the ILOCK until after the transaction commit like all other bmap updates do, and get rid of the dqread wrapper that does nothing but complicate the codebase. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
1 parent f4901a1 commit eae44cb

File tree

1 file changed

+28
-51
lines changed

1 file changed

+28
-51
lines changed

fs/xfs/xfs_dquot.c

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -289,13 +289,12 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp)
289289
*/
290290
STATIC int
291291
xfs_dquot_disk_alloc(
292-
struct xfs_trans **tpp,
293292
struct xfs_dquot *dqp,
294293
struct xfs_buf **bpp)
295294
{
296295
struct xfs_bmbt_irec map;
297-
struct xfs_trans *tp = *tpp;
298-
struct xfs_mount *mp = tp->t_mountp;
296+
struct xfs_trans *tp;
297+
struct xfs_mount *mp = dqp->q_mount;
299298
struct xfs_buf *bp;
300299
xfs_dqtype_t qtype = xfs_dquot_type(dqp);
301300
struct xfs_inode *quotip = xfs_quota_inode(mp, qtype);
@@ -304,29 +303,35 @@ xfs_dquot_disk_alloc(
304303

305304
trace_xfs_dqalloc(dqp);
306305

306+
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_qm_dqalloc,
307+
XFS_QM_DQALLOC_SPACE_RES(mp), 0, 0, &tp);
308+
if (error)
309+
return error;
310+
307311
xfs_ilock(quotip, XFS_ILOCK_EXCL);
312+
xfs_trans_ijoin(tp, quotip, 0);
313+
308314
if (!xfs_this_quota_on(dqp->q_mount, qtype)) {
309315
/*
310316
* Return if this type of quotas is turned off while we didn't
311317
* have an inode lock
312318
*/
313-
xfs_iunlock(quotip, XFS_ILOCK_EXCL);
314-
return -ESRCH;
319+
error = -ESRCH;
320+
goto err_cancel;
315321
}
316322

317-
xfs_trans_ijoin(tp, quotip, XFS_ILOCK_EXCL);
318-
319323
error = xfs_iext_count_may_overflow(quotip, XFS_DATA_FORK,
320324
XFS_IEXT_ADD_NOSPLIT_CNT);
321325
if (error)
322-
return error;
326+
goto err_cancel;
323327

324328
/* Create the block mapping. */
325329
error = xfs_bmapi_write(tp, quotip, dqp->q_fileoffset,
326330
XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA, 0, &map,
327331
&nmaps);
328332
if (error)
329-
return error;
333+
goto err_cancel;
334+
330335
ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB);
331336
ASSERT(nmaps == 1);
332337
ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
@@ -341,7 +346,7 @@ xfs_dquot_disk_alloc(
341346
error = xfs_trans_get_buf(tp, mp->m_ddev_targp, dqp->q_blkno,
342347
mp->m_quotainfo->qi_dqchunklen, 0, &bp);
343348
if (error)
344-
return error;
349+
goto err_cancel;
345350
bp->b_ops = &xfs_dquot_buf_ops;
346351

347352
/*
@@ -371,16 +376,25 @@ xfs_dquot_disk_alloc(
371376
* is responsible for unlocking any buffer passed back, either
372377
* manually or by committing the transaction. On error, the buffer is
373378
* released and not passed back.
379+
*
380+
* Keep the quota inode ILOCKed until after the transaction commit to
381+
* maintain the atomicity of bmap/rmap updates.
374382
*/
375383
xfs_trans_bhold(tp, bp);
376-
error = xfs_defer_finish(tpp);
384+
error = xfs_trans_commit(tp);
385+
xfs_iunlock(quotip, XFS_ILOCK_EXCL);
377386
if (error) {
378-
xfs_trans_bhold_release(*tpp, bp);
379-
xfs_trans_brelse(*tpp, bp);
387+
xfs_buf_relse(bp);
380388
return error;
381389
}
390+
382391
*bpp = bp;
383392
return 0;
393+
394+
err_cancel:
395+
xfs_trans_cancel(tp);
396+
xfs_iunlock(quotip, XFS_ILOCK_EXCL);
397+
return error;
384398
}
385399

386400
/*
@@ -629,43 +643,6 @@ xfs_dquot_to_disk(
629643
ddqp->d_rtbtimer = xfs_dquot_to_disk_ts(dqp, dqp->q_rtb.timer);
630644
}
631645

632-
/* Allocate and initialize the dquot buffer for this in-core dquot. */
633-
static int
634-
xfs_qm_dqread_alloc(
635-
struct xfs_mount *mp,
636-
struct xfs_dquot *dqp,
637-
struct xfs_buf **bpp)
638-
{
639-
struct xfs_trans *tp;
640-
int error;
641-
642-
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_qm_dqalloc,
643-
XFS_QM_DQALLOC_SPACE_RES(mp), 0, 0, &tp);
644-
if (error)
645-
goto err;
646-
647-
error = xfs_dquot_disk_alloc(&tp, dqp, bpp);
648-
if (error)
649-
goto err_cancel;
650-
651-
error = xfs_trans_commit(tp);
652-
if (error) {
653-
/*
654-
* Buffer was held to the transaction, so we have to unlock it
655-
* manually here because we're not passing it back.
656-
*/
657-
xfs_buf_relse(*bpp);
658-
*bpp = NULL;
659-
goto err;
660-
}
661-
return 0;
662-
663-
err_cancel:
664-
xfs_trans_cancel(tp);
665-
err:
666-
return error;
667-
}
668-
669646
/*
670647
* Read in the ondisk dquot using dqtobp() then copy it to an incore version,
671648
* and release the buffer immediately. If @can_alloc is true, fill any
@@ -689,7 +666,7 @@ xfs_qm_dqread(
689666
/* Try to read the buffer, allocating if necessary. */
690667
error = xfs_dquot_disk_read(mp, dqp, &bp);
691668
if (error == -ENOENT && can_alloc)
692-
error = xfs_qm_dqread_alloc(mp, dqp, &bp);
669+
error = xfs_dquot_disk_alloc(dqp, &bp);
693670
if (error)
694671
goto err;
695672

0 commit comments

Comments
 (0)