diff options
| author | Jan Kara <jack@suse.cz> | 2009-08-06 17:29:34 -0400 |
|---|---|---|
| committer | Joel Becker <joel.becker@oracle.com> | 2009-08-10 15:20:22 -0400 |
| commit | b409d7a0ab46fe530efe52734984b4ed5d46c3eb (patch) | |
| tree | f9f7861ff8b943ee905dc16a8290cac757df391e | |
| parent | 8a57a9dda760ea7845390f1cd36f3eb2a49391d8 (diff) | |
ocfs2: Fix possible deadlock when extending quota file
In OCFS2, allocator locks rank above transaction start. Thus we
cannot extend quota file from inside a transaction less we could
deadlock.
We solve the problem by starting transaction not already in
ocfs2_acquire_dquot() but only in ocfs2_local_read_dquot() and
ocfs2_global_read_dquot() and we allocate blocks to quota files before starting
the transaction. In case we crash, quota files will just have a few blocks
more but that's no problem since we just use them next time we extend the
quota file.
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
| -rw-r--r-- | fs/ocfs2/journal.h | 5 | ||||
| -rw-r--r-- | fs/ocfs2/quota_global.c | 115 |
2 files changed, 57 insertions, 63 deletions
diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 4a4d3b55fd22..2c3222aec622 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h | |||
| @@ -363,11 +363,6 @@ static inline int ocfs2_quota_trans_credits(struct super_block *sb) | |||
| 363 | return credits; | 363 | return credits; |
| 364 | } | 364 | } |
| 365 | 365 | ||
| 366 | /* Number of credits needed for removing quota structure from file */ | ||
| 367 | int ocfs2_calc_qdel_credits(struct super_block *sb, int type); | ||
| 368 | /* Number of credits needed for initialization of new quota structure */ | ||
| 369 | int ocfs2_calc_qinit_credits(struct super_block *sb, int type); | ||
| 370 | |||
| 371 | /* group extend. inode update and last group update. */ | 366 | /* group extend. inode update and last group update. */ |
| 372 | #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) | 367 | #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) |
| 373 | 368 | ||
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index d604a6aa0a22..bf7742d0ee3b 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c | |||
| @@ -215,11 +215,7 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, | |||
| 215 | loff_t rounded_end = | 215 | loff_t rounded_end = |
| 216 | ocfs2_align_bytes_to_blocks(sb, off + len); | 216 | ocfs2_align_bytes_to_blocks(sb, off + len); |
| 217 | 217 | ||
| 218 | down_write(&OCFS2_I(gqinode)->ip_alloc_sem); | 218 | /* Space is already allocated in ocfs2_global_read_dquot() */ |
| 219 | err = ocfs2_extend_no_holes(gqinode, rounded_end, off); | ||
| 220 | up_write(&OCFS2_I(gqinode)->ip_alloc_sem); | ||
| 221 | if (err < 0) | ||
| 222 | goto out; | ||
| 223 | err = ocfs2_simple_size_update(gqinode, | 219 | err = ocfs2_simple_size_update(gqinode, |
| 224 | oinfo->dqi_gqi_bh, | 220 | oinfo->dqi_gqi_bh, |
| 225 | rounded_end); | 221 | rounded_end); |
| @@ -405,13 +401,36 @@ int ocfs2_global_write_info(struct super_block *sb, int type) | |||
| 405 | return err; | 401 | return err; |
| 406 | } | 402 | } |
| 407 | 403 | ||
| 404 | static int ocfs2_global_qinit_alloc(struct super_block *sb, int type) | ||
| 405 | { | ||
| 406 | struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; | ||
| 407 | |||
| 408 | /* | ||
| 409 | * We may need to allocate tree blocks and a leaf block but not the | ||
| 410 | * root block | ||
| 411 | */ | ||
| 412 | return oinfo->dqi_gi.dqi_qtree_depth; | ||
| 413 | } | ||
| 414 | |||
| 415 | static int ocfs2_calc_global_qinit_credits(struct super_block *sb, int type) | ||
| 416 | { | ||
| 417 | /* We modify all the allocated blocks, tree root, and info block */ | ||
| 418 | return (ocfs2_global_qinit_alloc(sb, type) + 2) * | ||
| 419 | OCFS2_QUOTA_BLOCK_UPDATE_CREDITS; | ||
| 420 | } | ||
| 421 | |||
| 408 | /* Read in information from global quota file and acquire a reference to it. | 422 | /* Read in information from global quota file and acquire a reference to it. |
| 409 | * dquot_acquire() has already started the transaction and locked quota file */ | 423 | * dquot_acquire() has already started the transaction and locked quota file */ |
| 410 | int ocfs2_global_read_dquot(struct dquot *dquot) | 424 | int ocfs2_global_read_dquot(struct dquot *dquot) |
| 411 | { | 425 | { |
| 412 | int err, err2, ex = 0; | 426 | int err, err2, ex = 0; |
| 413 | struct ocfs2_mem_dqinfo *info = | 427 | struct super_block *sb = dquot->dq_sb; |
| 414 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | 428 | int type = dquot->dq_type; |
| 429 | struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv; | ||
| 430 | struct ocfs2_super *osb = OCFS2_SB(sb); | ||
| 431 | struct inode *gqinode = info->dqi_gqinode; | ||
| 432 | int need_alloc = ocfs2_global_qinit_alloc(sb, type); | ||
| 433 | handle_t *handle = NULL; | ||
| 415 | 434 | ||
| 416 | err = ocfs2_qinfo_lock(info, 0); | 435 | err = ocfs2_qinfo_lock(info, 0); |
| 417 | if (err < 0) | 436 | if (err < 0) |
| @@ -422,14 +441,33 @@ int ocfs2_global_read_dquot(struct dquot *dquot) | |||
| 422 | OCFS2_DQUOT(dquot)->dq_use_count++; | 441 | OCFS2_DQUOT(dquot)->dq_use_count++; |
| 423 | OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; | 442 | OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; |
| 424 | OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; | 443 | OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; |
| 444 | ocfs2_qinfo_unlock(info, 0); | ||
| 445 | |||
| 425 | if (!dquot->dq_off) { /* No real quota entry? */ | 446 | if (!dquot->dq_off) { /* No real quota entry? */ |
| 426 | /* Upgrade to exclusive lock for allocation */ | ||
| 427 | ocfs2_qinfo_unlock(info, 0); | ||
| 428 | err = ocfs2_qinfo_lock(info, 1); | ||
| 429 | if (err < 0) | ||
| 430 | goto out_qlock; | ||
| 431 | ex = 1; | 447 | ex = 1; |
| 448 | /* | ||
| 449 | * Add blocks to quota file before we start a transaction since | ||
| 450 | * locking allocators ranks above a transaction start | ||
| 451 | */ | ||
| 452 | WARN_ON(journal_current_handle()); | ||
| 453 | down_write(&OCFS2_I(gqinode)->ip_alloc_sem); | ||
| 454 | err = ocfs2_extend_no_holes(gqinode, | ||
| 455 | gqinode->i_size + (need_alloc << sb->s_blocksize_bits), | ||
| 456 | gqinode->i_size); | ||
| 457 | up_write(&OCFS2_I(gqinode)->ip_alloc_sem); | ||
| 458 | if (err < 0) | ||
| 459 | goto out; | ||
| 460 | } | ||
| 461 | |||
| 462 | handle = ocfs2_start_trans(osb, | ||
| 463 | ocfs2_calc_global_qinit_credits(sb, type)); | ||
| 464 | if (IS_ERR(handle)) { | ||
| 465 | err = PTR_ERR(handle); | ||
| 466 | goto out; | ||
| 432 | } | 467 | } |
| 468 | err = ocfs2_qinfo_lock(info, ex); | ||
| 469 | if (err < 0) | ||
| 470 | goto out_trans; | ||
| 433 | err = qtree_write_dquot(&info->dqi_gi, dquot); | 471 | err = qtree_write_dquot(&info->dqi_gi, dquot); |
| 434 | if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) { | 472 | if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) { |
| 435 | err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type); | 473 | err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type); |
| @@ -441,6 +479,9 @@ out_qlock: | |||
| 441 | ocfs2_qinfo_unlock(info, 1); | 479 | ocfs2_qinfo_unlock(info, 1); |
| 442 | else | 480 | else |
| 443 | ocfs2_qinfo_unlock(info, 0); | 481 | ocfs2_qinfo_unlock(info, 0); |
| 482 | out_trans: | ||
| 483 | if (handle) | ||
| 484 | ocfs2_commit_trans(osb, handle); | ||
| 444 | out: | 485 | out: |
| 445 | if (err < 0) | 486 | if (err < 0) |
| 446 | mlog_errno(err); | 487 | mlog_errno(err); |
| @@ -638,24 +679,17 @@ out: | |||
| 638 | return status; | 679 | return status; |
| 639 | } | 680 | } |
| 640 | 681 | ||
| 641 | int ocfs2_calc_qdel_credits(struct super_block *sb, int type) | 682 | static int ocfs2_calc_qdel_credits(struct super_block *sb, int type) |
| 642 | { | 683 | { |
| 643 | struct ocfs2_mem_dqinfo *oinfo; | 684 | struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; |
| 644 | int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
| 645 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; | ||
| 646 | |||
| 647 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type])) | ||
| 648 | return 0; | ||
| 649 | |||
| 650 | oinfo = sb_dqinfo(sb, type)->dqi_priv; | ||
| 651 | /* | 685 | /* |
| 652 | * We modify tree, leaf block, global info, local chunk header, | 686 | * We modify tree, leaf block, global info, local chunk header, |
| 653 | * global and local inode; OCFS2_QINFO_WRITE_CREDITS already | 687 | * global and local inode; OCFS2_QINFO_WRITE_CREDITS already |
| 654 | * accounts for inode update | 688 | * accounts for inode update |
| 655 | */ | 689 | */ |
| 656 | return oinfo->dqi_gi.dqi_qtree_depth + | 690 | return (oinfo->dqi_gi.dqi_qtree_depth + 2) * |
| 691 | OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + | ||
| 657 | OCFS2_QINFO_WRITE_CREDITS + | 692 | OCFS2_QINFO_WRITE_CREDITS + |
| 658 | 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + | ||
| 659 | OCFS2_INODE_UPDATE_CREDITS; | 693 | OCFS2_INODE_UPDATE_CREDITS; |
| 660 | } | 694 | } |
| 661 | 695 | ||
| @@ -688,36 +722,10 @@ out: | |||
| 688 | return status; | 722 | return status; |
| 689 | } | 723 | } |
| 690 | 724 | ||
| 691 | int ocfs2_calc_qinit_credits(struct super_block *sb, int type) | ||
| 692 | { | ||
| 693 | struct ocfs2_mem_dqinfo *oinfo; | ||
| 694 | int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
| 695 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; | ||
| 696 | struct ocfs2_dinode *lfe, *gfe; | ||
| 697 | |||
| 698 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type])) | ||
| 699 | return 0; | ||
| 700 | |||
| 701 | oinfo = sb_dqinfo(sb, type)->dqi_priv; | ||
| 702 | gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data; | ||
| 703 | lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data; | ||
| 704 | /* We can extend local file + global file. In local file we | ||
| 705 | * can modify info, chunk header block and dquot block. In | ||
| 706 | * global file we can modify info, tree and leaf block */ | ||
| 707 | return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) + | ||
| 708 | ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) + | ||
| 709 | OCFS2_LOCAL_QINFO_WRITE_CREDITS + | ||
| 710 | 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + | ||
| 711 | oinfo->dqi_gi.dqi_qtree_depth + | ||
| 712 | 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS; | ||
| 713 | } | ||
| 714 | |||
| 715 | static int ocfs2_acquire_dquot(struct dquot *dquot) | 725 | static int ocfs2_acquire_dquot(struct dquot *dquot) |
| 716 | { | 726 | { |
| 717 | handle_t *handle; | ||
| 718 | struct ocfs2_mem_dqinfo *oinfo = | 727 | struct ocfs2_mem_dqinfo *oinfo = |
| 719 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | 728 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; |
| 720 | struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb); | ||
| 721 | int status = 0; | 729 | int status = 0; |
| 722 | 730 | ||
| 723 | mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type); | 731 | mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type); |
| @@ -726,16 +734,7 @@ static int ocfs2_acquire_dquot(struct dquot *dquot) | |||
| 726 | status = ocfs2_lock_global_qf(oinfo, 1); | 734 | status = ocfs2_lock_global_qf(oinfo, 1); |
| 727 | if (status < 0) | 735 | if (status < 0) |
| 728 | goto out; | 736 | goto out; |
| 729 | handle = ocfs2_start_trans(osb, | ||
| 730 | ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type)); | ||
| 731 | if (IS_ERR(handle)) { | ||
| 732 | status = PTR_ERR(handle); | ||
| 733 | mlog_errno(status); | ||
| 734 | goto out_ilock; | ||
| 735 | } | ||
| 736 | status = dquot_acquire(dquot); | 737 | status = dquot_acquire(dquot); |
| 737 | ocfs2_commit_trans(osb, handle); | ||
| 738 | out_ilock: | ||
| 739 | ocfs2_unlock_global_qf(oinfo, 1); | 738 | ocfs2_unlock_global_qf(oinfo, 1); |
| 740 | out: | 739 | out: |
| 741 | mlog_exit(status); | 740 | mlog_exit(status); |
