diff options
author | Tim Shimmin <tes@sgi.com> | 2005-09-04 18:29:01 -0400 |
---|---|---|
committer | Nathan Scott <nathans@sgi.com> | 2005-09-04 18:29:01 -0400 |
commit | efa092f3d4c60be7e81de515db9f06e5f8426afc (patch) | |
tree | 6955d8a48cb52effb2f2352fab34bdf2291acce3 /fs | |
parent | 0f9fffbcc1817c655d6dd40960ae2e0086b0f64f (diff) |
[XFS] Fixes a bug in the quota code when allocating a new dquot record
which can cause an extent hole to be filled and a free extent to be
processed. In this case, we make a few mistakes: forget to pass back the
transaction, forget to put a hold on the buffer and forget to add the buf
to the new transaction.
SGI-PV: 940366
SGI-Modid: xfs-linux:xfs-kern:23594a
Signed-off-by: Tim Shimmin <tes@sgi.com>
Signed-off-by: Nathan Scott <nathans@sgi.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/quota/xfs_dquot.c | 43 | ||||
-rw-r--r-- | fs/xfs/xfs_trans.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_trans_buf.c | 23 |
3 files changed, 60 insertions, 7 deletions
diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 46ce1e3ce1d6..e2e8d35fa4d0 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c | |||
@@ -421,7 +421,7 @@ xfs_qm_init_dquot_blk( | |||
421 | */ | 421 | */ |
422 | STATIC int | 422 | STATIC int |
423 | xfs_qm_dqalloc( | 423 | xfs_qm_dqalloc( |
424 | xfs_trans_t *tp, | 424 | xfs_trans_t **tpp, |
425 | xfs_mount_t *mp, | 425 | xfs_mount_t *mp, |
426 | xfs_dquot_t *dqp, | 426 | xfs_dquot_t *dqp, |
427 | xfs_inode_t *quotip, | 427 | xfs_inode_t *quotip, |
@@ -433,6 +433,7 @@ xfs_qm_dqalloc( | |||
433 | xfs_bmbt_irec_t map; | 433 | xfs_bmbt_irec_t map; |
434 | int nmaps, error, committed; | 434 | int nmaps, error, committed; |
435 | xfs_buf_t *bp; | 435 | xfs_buf_t *bp; |
436 | xfs_trans_t *tp = *tpp; | ||
436 | 437 | ||
437 | ASSERT(tp != NULL); | 438 | ASSERT(tp != NULL); |
438 | xfs_dqtrace_entry(dqp, "DQALLOC"); | 439 | xfs_dqtrace_entry(dqp, "DQALLOC"); |
@@ -492,10 +493,32 @@ xfs_qm_dqalloc( | |||
492 | xfs_qm_init_dquot_blk(tp, mp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT), | 493 | xfs_qm_init_dquot_blk(tp, mp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT), |
493 | dqp->dq_flags & XFS_DQ_ALLTYPES, bp); | 494 | dqp->dq_flags & XFS_DQ_ALLTYPES, bp); |
494 | 495 | ||
495 | if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed))) { | 496 | /* |
497 | * xfs_bmap_finish() may commit the current transaction and | ||
498 | * start a second transaction if the freelist is not empty. | ||
499 | * | ||
500 | * Since we still want to modify this buffer, we need to | ||
501 | * ensure that the buffer is not released on commit of | ||
502 | * the first transaction and ensure the buffer is added to the | ||
503 | * second transaction. | ||
504 | * | ||
505 | * If there is only one transaction then don't stop the buffer | ||
506 | * from being released when it commits later on. | ||
507 | */ | ||
508 | |||
509 | xfs_trans_bhold(tp, bp); | ||
510 | |||
511 | if ((error = xfs_bmap_finish(tpp, &flist, firstblock, &committed))) { | ||
496 | goto error1; | 512 | goto error1; |
497 | } | 513 | } |
498 | 514 | ||
515 | if (committed) { | ||
516 | tp = *tpp; | ||
517 | xfs_trans_bjoin(tp, bp); | ||
518 | } else { | ||
519 | xfs_trans_bhold_release(tp, bp); | ||
520 | } | ||
521 | |||
499 | *O_bpp = bp; | 522 | *O_bpp = bp; |
500 | return 0; | 523 | return 0; |
501 | 524 | ||
@@ -514,7 +537,7 @@ xfs_qm_dqalloc( | |||
514 | */ | 537 | */ |
515 | STATIC int | 538 | STATIC int |
516 | xfs_qm_dqtobp( | 539 | xfs_qm_dqtobp( |
517 | xfs_trans_t *tp, | 540 | xfs_trans_t **tpp, |
518 | xfs_dquot_t *dqp, | 541 | xfs_dquot_t *dqp, |
519 | xfs_disk_dquot_t **O_ddpp, | 542 | xfs_disk_dquot_t **O_ddpp, |
520 | xfs_buf_t **O_bpp, | 543 | xfs_buf_t **O_bpp, |
@@ -528,6 +551,7 @@ xfs_qm_dqtobp( | |||
528 | xfs_disk_dquot_t *ddq; | 551 | xfs_disk_dquot_t *ddq; |
529 | xfs_dqid_t id; | 552 | xfs_dqid_t id; |
530 | boolean_t newdquot; | 553 | boolean_t newdquot; |
554 | xfs_trans_t *tp = (tpp ? *tpp : NULL); | ||
531 | 555 | ||
532 | mp = dqp->q_mount; | 556 | mp = dqp->q_mount; |
533 | id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT); | 557 | id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT); |
@@ -579,9 +603,10 @@ xfs_qm_dqtobp( | |||
579 | return (ENOENT); | 603 | return (ENOENT); |
580 | 604 | ||
581 | ASSERT(tp); | 605 | ASSERT(tp); |
582 | if ((error = xfs_qm_dqalloc(tp, mp, dqp, quotip, | 606 | if ((error = xfs_qm_dqalloc(tpp, mp, dqp, quotip, |
583 | dqp->q_fileoffset, &bp))) | 607 | dqp->q_fileoffset, &bp))) |
584 | return (error); | 608 | return (error); |
609 | tp = *tpp; | ||
585 | newdquot = B_TRUE; | 610 | newdquot = B_TRUE; |
586 | } else { | 611 | } else { |
587 | /* | 612 | /* |
@@ -645,7 +670,7 @@ xfs_qm_dqtobp( | |||
645 | /* ARGSUSED */ | 670 | /* ARGSUSED */ |
646 | STATIC int | 671 | STATIC int |
647 | xfs_qm_dqread( | 672 | xfs_qm_dqread( |
648 | xfs_trans_t *tp, | 673 | xfs_trans_t **tpp, |
649 | xfs_dqid_t id, | 674 | xfs_dqid_t id, |
650 | xfs_dquot_t *dqp, /* dquot to get filled in */ | 675 | xfs_dquot_t *dqp, /* dquot to get filled in */ |
651 | uint flags) | 676 | uint flags) |
@@ -653,15 +678,19 @@ xfs_qm_dqread( | |||
653 | xfs_disk_dquot_t *ddqp; | 678 | xfs_disk_dquot_t *ddqp; |
654 | xfs_buf_t *bp; | 679 | xfs_buf_t *bp; |
655 | int error; | 680 | int error; |
681 | xfs_trans_t *tp; | ||
682 | |||
683 | ASSERT(tpp); | ||
656 | 684 | ||
657 | /* | 685 | /* |
658 | * get a pointer to the on-disk dquot and the buffer containing it | 686 | * get a pointer to the on-disk dquot and the buffer containing it |
659 | * dqp already knows its own type (GROUP/USER). | 687 | * dqp already knows its own type (GROUP/USER). |
660 | */ | 688 | */ |
661 | xfs_dqtrace_entry(dqp, "DQREAD"); | 689 | xfs_dqtrace_entry(dqp, "DQREAD"); |
662 | if ((error = xfs_qm_dqtobp(tp, dqp, &ddqp, &bp, flags))) { | 690 | if ((error = xfs_qm_dqtobp(tpp, dqp, &ddqp, &bp, flags))) { |
663 | return (error); | 691 | return (error); |
664 | } | 692 | } |
693 | tp = *tpp; | ||
665 | 694 | ||
666 | /* copy everything from disk dquot to the incore dquot */ | 695 | /* copy everything from disk dquot to the incore dquot */ |
667 | memcpy(&dqp->q_core, ddqp, sizeof(xfs_disk_dquot_t)); | 696 | memcpy(&dqp->q_core, ddqp, sizeof(xfs_disk_dquot_t)); |
@@ -740,7 +769,7 @@ xfs_qm_idtodq( | |||
740 | * Read it from disk; xfs_dqread() takes care of | 769 | * Read it from disk; xfs_dqread() takes care of |
741 | * all the necessary initialization of dquot's fields (locks, etc) | 770 | * all the necessary initialization of dquot's fields (locks, etc) |
742 | */ | 771 | */ |
743 | if ((error = xfs_qm_dqread(tp, id, dqp, flags))) { | 772 | if ((error = xfs_qm_dqread(&tp, id, dqp, flags))) { |
744 | /* | 773 | /* |
745 | * This can happen if quotas got turned off (ESRCH), | 774 | * This can happen if quotas got turned off (ESRCH), |
746 | * or if the dquot didn't exist on disk and we ask to | 775 | * or if the dquot didn't exist on disk and we ask to |
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 9ee5eeee8026..a263aec8b3a6 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h | |||
@@ -999,6 +999,7 @@ struct xfs_buf *xfs_trans_getsb(xfs_trans_t *, struct xfs_mount *, int); | |||
999 | void xfs_trans_brelse(xfs_trans_t *, struct xfs_buf *); | 999 | void xfs_trans_brelse(xfs_trans_t *, struct xfs_buf *); |
1000 | void xfs_trans_bjoin(xfs_trans_t *, struct xfs_buf *); | 1000 | void xfs_trans_bjoin(xfs_trans_t *, struct xfs_buf *); |
1001 | void xfs_trans_bhold(xfs_trans_t *, struct xfs_buf *); | 1001 | void xfs_trans_bhold(xfs_trans_t *, struct xfs_buf *); |
1002 | void xfs_trans_bhold_release(xfs_trans_t *, struct xfs_buf *); | ||
1002 | void xfs_trans_binval(xfs_trans_t *, struct xfs_buf *); | 1003 | void xfs_trans_binval(xfs_trans_t *, struct xfs_buf *); |
1003 | void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *); | 1004 | void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *); |
1004 | void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *); | 1005 | void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *); |
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 144da7a85466..e733293dd7f4 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c | |||
@@ -714,6 +714,29 @@ xfs_trans_bhold(xfs_trans_t *tp, | |||
714 | } | 714 | } |
715 | 715 | ||
716 | /* | 716 | /* |
717 | * Cancel the previous buffer hold request made on this buffer | ||
718 | * for this transaction. | ||
719 | */ | ||
720 | void | ||
721 | xfs_trans_bhold_release(xfs_trans_t *tp, | ||
722 | xfs_buf_t *bp) | ||
723 | { | ||
724 | xfs_buf_log_item_t *bip; | ||
725 | |||
726 | ASSERT(XFS_BUF_ISBUSY(bp)); | ||
727 | ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp); | ||
728 | ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL); | ||
729 | |||
730 | bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *); | ||
731 | ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); | ||
732 | ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL)); | ||
733 | ASSERT(atomic_read(&bip->bli_refcount) > 0); | ||
734 | ASSERT(bip->bli_flags & XFS_BLI_HOLD); | ||
735 | bip->bli_flags &= ~XFS_BLI_HOLD; | ||
736 | xfs_buf_item_trace("BHOLD RELEASE", bip); | ||
737 | } | ||
738 | |||
739 | /* | ||
717 | * This is called to mark bytes first through last inclusive of the given | 740 | * This is called to mark bytes first through last inclusive of the given |
718 | * buffer as needing to be logged when the transaction is committed. | 741 | * buffer as needing to be logged when the transaction is committed. |
719 | * The buffer must already be associated with the given transaction. | 742 | * The buffer must already be associated with the given transaction. |