aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2/file.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2009-06-02 08:24:01 -0400
committerJoel Becker <joel.becker@oracle.com>2009-06-03 22:14:29 -0400
commit65bac575e35915801ea518b9d8d8824367d125c8 (patch)
treebce57755cf8b9f783cdab046ea519c0e41f595e1 /fs/ocfs2/file.c
parentb4c30de39a2596503e888a7b47d19792f25913d6 (diff)
ocfs2: Fix possible deadlock with quotas in ocfs2_setattr()
We called vfs_dq_transfer() with global quota file lock held. This can lead to deadlocks as if vfs_dq_transfer() has to allocate new quota structure, it calls ocfs2_dquot_acquire() which tries to get quota file lock again and this can block if another node requested the lock in the mean time. Since we have to call vfs_dq_transfer() with transaction already started and quota file lock ranks above the transaction start, we cannot just rely on ocfs2_dquot_acquire() or ocfs2_dquot_release() on getting the lock if they need it. We fix the problem by acquiring pointers to all quota structures needed by vfs_dq_transfer() already before calling the function. By this we are sure that all quota structures are properly allocated and they can be freed only after we drop references to them. Thus we don't need quota file lock anywhere inside vfs_dq_transfer(). Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Joel Becker <joel.becker@oracle.com>
Diffstat (limited to 'fs/ocfs2/file.c')
-rw-r--r--fs/ocfs2/file.c53
1 files changed, 30 insertions, 23 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index c2a87c885b73..1a96cac31791 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -894,9 +894,9 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
894 struct ocfs2_super *osb = OCFS2_SB(sb); 894 struct ocfs2_super *osb = OCFS2_SB(sb);
895 struct buffer_head *bh = NULL; 895 struct buffer_head *bh = NULL;
896 handle_t *handle = NULL; 896 handle_t *handle = NULL;
897 int locked[MAXQUOTAS] = {0, 0}; 897 int qtype;
898 int credits, qtype; 898 struct dquot *transfer_from[MAXQUOTAS] = { };
899 struct ocfs2_mem_dqinfo *oinfo; 899 struct dquot *transfer_to[MAXQUOTAS] = { };
900 900
901 mlog_entry("(0x%p, '%.*s')\n", dentry, 901 mlog_entry("(0x%p, '%.*s')\n", dentry,
902 dentry->d_name.len, dentry->d_name.name); 902 dentry->d_name.len, dentry->d_name.name);
@@ -969,30 +969,37 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
969 969
970 if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || 970 if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
971 (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { 971 (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
972 credits = OCFS2_INODE_UPDATE_CREDITS; 972 /*
973 * Gather pointers to quota structures so that allocation /
974 * freeing of quota structures happens here and not inside
975 * vfs_dq_transfer() where we have problems with lock ordering
976 */
973 if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid 977 if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
974 && OCFS2_HAS_RO_COMPAT_FEATURE(sb, 978 && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
975 OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { 979 OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
976 oinfo = sb_dqinfo(sb, USRQUOTA)->dqi_priv; 980 transfer_to[USRQUOTA] = dqget(sb, attr->ia_uid,
977 status = ocfs2_lock_global_qf(oinfo, 1); 981 USRQUOTA);
978 if (status < 0) 982 transfer_from[USRQUOTA] = dqget(sb, inode->i_uid,
983 USRQUOTA);
984 if (!transfer_to[USRQUOTA] || !transfer_from[USRQUOTA]) {
985 status = -ESRCH;
979 goto bail_unlock; 986 goto bail_unlock;
980 credits += ocfs2_calc_qinit_credits(sb, USRQUOTA) + 987 }
981 ocfs2_calc_qdel_credits(sb, USRQUOTA);
982 locked[USRQUOTA] = 1;
983 } 988 }
984 if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid 989 if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
985 && OCFS2_HAS_RO_COMPAT_FEATURE(sb, 990 && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
986 OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { 991 OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
987 oinfo = sb_dqinfo(sb, GRPQUOTA)->dqi_priv; 992 transfer_to[GRPQUOTA] = dqget(sb, attr->ia_gid,
988 status = ocfs2_lock_global_qf(oinfo, 1); 993 GRPQUOTA);
989 if (status < 0) 994 transfer_from[GRPQUOTA] = dqget(sb, inode->i_gid,
995 GRPQUOTA);
996 if (!transfer_to[GRPQUOTA] || !transfer_from[GRPQUOTA]) {
997 status = -ESRCH;
990 goto bail_unlock; 998 goto bail_unlock;
991 credits += ocfs2_calc_qinit_credits(sb, GRPQUOTA) + 999 }
992 ocfs2_calc_qdel_credits(sb, GRPQUOTA);
993 locked[GRPQUOTA] = 1;
994 } 1000 }
995 handle = ocfs2_start_trans(osb, credits); 1001 handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS +
1002 2 * ocfs2_quota_trans_credits(sb));
996 if (IS_ERR(handle)) { 1003 if (IS_ERR(handle)) {
997 status = PTR_ERR(handle); 1004 status = PTR_ERR(handle);
998 mlog_errno(status); 1005 mlog_errno(status);
@@ -1030,12 +1037,6 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
1030bail_commit: 1037bail_commit:
1031 ocfs2_commit_trans(osb, handle); 1038 ocfs2_commit_trans(osb, handle);
1032bail_unlock: 1039bail_unlock:
1033 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
1034 if (!locked[qtype])
1035 continue;
1036 oinfo = sb_dqinfo(sb, qtype)->dqi_priv;
1037 ocfs2_unlock_global_qf(oinfo, 1);
1038 }
1039 ocfs2_inode_unlock(inode, 1); 1040 ocfs2_inode_unlock(inode, 1);
1040bail_unlock_rw: 1041bail_unlock_rw:
1041 if (size_change) 1042 if (size_change)
@@ -1043,6 +1044,12 @@ bail_unlock_rw:
1043bail: 1044bail:
1044 brelse(bh); 1045 brelse(bh);
1045 1046
1047 /* Release quota pointers in case we acquired them */
1048 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
1049 dqput(transfer_to[qtype]);
1050 dqput(transfer_from[qtype]);
1051 }
1052
1046 if (!status && attr->ia_valid & ATTR_MODE) { 1053 if (!status && attr->ia_valid & ATTR_MODE) {
1047 status = ocfs2_acl_chmod(inode); 1054 status = ocfs2_acl_chmod(inode);
1048 if (status < 0) 1055 if (status < 0)