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 | |
| 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>
| -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. |
