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 /fs/ocfs2 | |
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>
Diffstat (limited to 'fs/ocfs2')
-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); |