aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2009-08-06 17:29:34 -0400
committerJoel Becker <joel.becker@oracle.com>2009-08-10 15:20:22 -0400
commitb409d7a0ab46fe530efe52734984b4ed5d46c3eb (patch)
treef9f7861ff8b943ee905dc16a8290cac757df391e /fs
parent8a57a9dda760ea7845390f1cd36f3eb2a49391d8 (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')
-rw-r--r--fs/ocfs2/journal.h5
-rw-r--r--fs/ocfs2/quota_global.c115
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 */
367int ocfs2_calc_qdel_credits(struct super_block *sb, int type);
368/* Number of credits needed for initialization of new quota structure */
369int 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
404static 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
415static 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 */
410int ocfs2_global_read_dquot(struct dquot *dquot) 424int 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);
482out_trans:
483 if (handle)
484 ocfs2_commit_trans(osb, handle);
444out: 485out:
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
641int ocfs2_calc_qdel_credits(struct super_block *sb, int type) 682static 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
691int 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
715static int ocfs2_acquire_dquot(struct dquot *dquot) 725static 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);
738out_ilock:
739 ocfs2_unlock_global_qf(oinfo, 1); 738 ocfs2_unlock_global_qf(oinfo, 1);
740out: 739out:
741 mlog_exit(status); 740 mlog_exit(status);