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>

authored by Jan Kara and committed by Joel Becker b409d7a0 8a57a9dd

+57 -63
-5
fs/ocfs2/journal.h
··· 363 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 366 /* group extend. inode update and last group update. */ 372 367 #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) 373 368
+57 -58
fs/ocfs2/quota_global.c
··· 215 215 loff_t rounded_end = 216 216 ocfs2_align_bytes_to_blocks(sb, off + len); 217 217 218 - down_write(&OCFS2_I(gqinode)->ip_alloc_sem); 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; 218 + /* Space is already allocated in ocfs2_global_read_dquot() */ 223 219 err = ocfs2_simple_size_update(gqinode, 224 220 oinfo->dqi_gqi_bh, 225 221 rounded_end); ··· 401 405 return err; 402 406 } 403 407 408 + static int ocfs2_global_qinit_alloc(struct super_block *sb, int type) 409 + { 410 + struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; 411 + 412 + /* 413 + * We may need to allocate tree blocks and a leaf block but not the 414 + * root block 415 + */ 416 + return oinfo->dqi_gi.dqi_qtree_depth; 417 + } 418 + 419 + static int ocfs2_calc_global_qinit_credits(struct super_block *sb, int type) 420 + { 421 + /* We modify all the allocated blocks, tree root, and info block */ 422 + return (ocfs2_global_qinit_alloc(sb, type) + 2) * 423 + OCFS2_QUOTA_BLOCK_UPDATE_CREDITS; 424 + } 425 + 404 426 /* Read in information from global quota file and acquire a reference to it. 405 427 * dquot_acquire() has already started the transaction and locked quota file */ 406 428 int ocfs2_global_read_dquot(struct dquot *dquot) 407 429 { 408 430 int err, err2, ex = 0; 409 - struct ocfs2_mem_dqinfo *info = 410 - sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; 431 + struct super_block *sb = dquot->dq_sb; 432 + int type = dquot->dq_type; 433 + struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv; 434 + struct ocfs2_super *osb = OCFS2_SB(sb); 435 + struct inode *gqinode = info->dqi_gqinode; 436 + int need_alloc = ocfs2_global_qinit_alloc(sb, type); 437 + handle_t *handle = NULL; 411 438 412 439 err = ocfs2_qinfo_lock(info, 0); 413 440 if (err < 0) ··· 441 422 OCFS2_DQUOT(dquot)->dq_use_count++; 442 423 OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; 443 424 OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; 425 + ocfs2_qinfo_unlock(info, 0); 426 + 444 427 if (!dquot->dq_off) { /* No real quota entry? */ 445 - /* Upgrade to exclusive lock for allocation */ 446 - ocfs2_qinfo_unlock(info, 0); 447 - err = ocfs2_qinfo_lock(info, 1); 448 - if (err < 0) 449 - goto out_qlock; 450 428 ex = 1; 429 + /* 430 + * Add blocks to quota file before we start a transaction since 431 + * locking allocators ranks above a transaction start 432 + */ 433 + WARN_ON(journal_current_handle()); 434 + down_write(&OCFS2_I(gqinode)->ip_alloc_sem); 435 + err = ocfs2_extend_no_holes(gqinode, 436 + gqinode->i_size + (need_alloc << sb->s_blocksize_bits), 437 + gqinode->i_size); 438 + up_write(&OCFS2_I(gqinode)->ip_alloc_sem); 439 + if (err < 0) 440 + goto out; 451 441 } 442 + 443 + handle = ocfs2_start_trans(osb, 444 + ocfs2_calc_global_qinit_credits(sb, type)); 445 + if (IS_ERR(handle)) { 446 + err = PTR_ERR(handle); 447 + goto out; 448 + } 449 + err = ocfs2_qinfo_lock(info, ex); 450 + if (err < 0) 451 + goto out_trans; 452 452 err = qtree_write_dquot(&info->dqi_gi, dquot); 453 453 if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) { 454 454 err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type); ··· 479 441 ocfs2_qinfo_unlock(info, 1); 480 442 else 481 443 ocfs2_qinfo_unlock(info, 0); 444 + out_trans: 445 + if (handle) 446 + ocfs2_commit_trans(osb, handle); 482 447 out: 483 448 if (err < 0) 484 449 mlog_errno(err); ··· 679 638 return status; 680 639 } 681 640 682 - int ocfs2_calc_qdel_credits(struct super_block *sb, int type) 641 + static int ocfs2_calc_qdel_credits(struct super_block *sb, int type) 683 642 { 684 - struct ocfs2_mem_dqinfo *oinfo; 685 - int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, 686 - OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; 687 - 688 - if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type])) 689 - return 0; 690 - 691 - oinfo = sb_dqinfo(sb, type)->dqi_priv; 643 + struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv; 692 644 /* 693 645 * We modify tree, leaf block, global info, local chunk header, 694 646 * global and local inode; OCFS2_QINFO_WRITE_CREDITS already 695 647 * accounts for inode update 696 648 */ 697 - return oinfo->dqi_gi.dqi_qtree_depth + 649 + return (oinfo->dqi_gi.dqi_qtree_depth + 2) * 650 + OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + 698 651 OCFS2_QINFO_WRITE_CREDITS + 699 - 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + 700 652 OCFS2_INODE_UPDATE_CREDITS; 701 653 } 702 654 ··· 722 688 return status; 723 689 } 724 690 725 - int ocfs2_calc_qinit_credits(struct super_block *sb, int type) 726 - { 727 - struct ocfs2_mem_dqinfo *oinfo; 728 - int features[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, 729 - OCFS2_FEATURE_RO_COMPAT_GRPQUOTA }; 730 - struct ocfs2_dinode *lfe, *gfe; 731 - 732 - if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, features[type])) 733 - return 0; 734 - 735 - oinfo = sb_dqinfo(sb, type)->dqi_priv; 736 - gfe = (struct ocfs2_dinode *)oinfo->dqi_gqi_bh->b_data; 737 - lfe = (struct ocfs2_dinode *)oinfo->dqi_lqi_bh->b_data; 738 - /* We can extend local file + global file. In local file we 739 - * can modify info, chunk header block and dquot block. In 740 - * global file we can modify info, tree and leaf block */ 741 - return ocfs2_calc_extend_credits(sb, &lfe->id2.i_list, 0) + 742 - ocfs2_calc_extend_credits(sb, &gfe->id2.i_list, 0) + 743 - OCFS2_LOCAL_QINFO_WRITE_CREDITS + 744 - 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS + 745 - oinfo->dqi_gi.dqi_qtree_depth + 746 - 2 * OCFS2_QUOTA_BLOCK_UPDATE_CREDITS; 747 - } 748 - 749 691 static int ocfs2_acquire_dquot(struct dquot *dquot) 750 692 { 751 - handle_t *handle; 752 693 struct ocfs2_mem_dqinfo *oinfo = 753 694 sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; 754 - struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb); 755 695 int status = 0; 756 696 757 697 mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type); ··· 734 726 status = ocfs2_lock_global_qf(oinfo, 1); 735 727 if (status < 0) 736 728 goto out; 737 - handle = ocfs2_start_trans(osb, 738 - ocfs2_calc_qinit_credits(dquot->dq_sb, dquot->dq_type)); 739 - if (IS_ERR(handle)) { 740 - status = PTR_ERR(handle); 741 - mlog_errno(status); 742 - goto out_ilock; 743 - } 744 729 status = dquot_acquire(dquot); 745 - ocfs2_commit_trans(osb, handle); 746 - out_ilock: 747 730 ocfs2_unlock_global_qf(oinfo, 1); 748 731 out: 749 732 mlog_exit(status);