aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2/quota_local.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2010-03-31 10:25:37 -0400
committerJan Kara <jack@suse.cz>2010-05-21 13:30:47 -0400
commitfb8dd8d780140a3f0e9074831a59054fec6cc451 (patch)
tree09e9f7bf157784fc6b0a7df71c1fbfe711349055 /fs/ocfs2/quota_local.c
parentae4f6ef13417deaa49471c0e903914a3ef3be258 (diff)
ocfs2: Fix quota locking
OCFS2 had three issues with quota locking: a) When reading dquot from global quota file, we started a transaction while holding dqio_mutex which is prone to deadlocks because other paths do it the other way around b) During ocfs2_sync_dquot we were not protected against concurrent writers on the same node. Because we first copy data to local buffer, a race could happen resulting in old data being written to global quota file and thus causing quota inconsistency after a crash. c) ip_alloc_sem of quota files was acquired while a transaction is started in ocfs2_quota_write which can deadlock because we first get ip_alloc_sem and then start a transaction when extending quota files. We fix the problem a) by pulling all necessary code to ocfs2_acquire_dquot and ocfs2_release_dquot. Thus we no longer depend on generic dquot_acquire to do the locking and can force proper lock ordering. Problems b) and c) are fixed by locking i_mutex and ip_alloc_sem of global quota file in ocfs2_lock_global_qf and removing ip_alloc_sem from ocfs2_quota_read and ocfs2_quota_write. Acked-by: Joel Becker <Joel.Becker@oracle.com> Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ocfs2/quota_local.c')
-rw-r--r--fs/ocfs2/quota_local.c88
1 files changed, 40 insertions, 48 deletions
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 962e8380852b..778947f0e951 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -22,6 +22,7 @@
22#include "dlmglue.h" 22#include "dlmglue.h"
23#include "quota.h" 23#include "quota.h"
24#include "uptodate.h" 24#include "uptodate.h"
25#include "super.h"
25 26
26/* Number of local quota structures per block */ 27/* Number of local quota structures per block */
27static inline unsigned int ol_quota_entries_per_block(struct super_block *sb) 28static inline unsigned int ol_quota_entries_per_block(struct super_block *sb)
@@ -129,6 +130,39 @@ static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh,
129 return 0; 130 return 0;
130} 131}
131 132
133/*
134 * Read quota block from a given logical offset.
135 *
136 * This function acquires ip_alloc_sem and thus it must not be called with a
137 * transaction started.
138 */
139static int ocfs2_read_quota_block(struct inode *inode, u64 v_block,
140 struct buffer_head **bh)
141{
142 int rc = 0;
143 struct buffer_head *tmp = *bh;
144
145 if (i_size_read(inode) >> inode->i_sb->s_blocksize_bits <= v_block) {
146 ocfs2_error(inode->i_sb,
147 "Quota file %llu is probably corrupted! Requested "
148 "to read block %Lu but file has size only %Lu\n",
149 (unsigned long long)OCFS2_I(inode)->ip_blkno,
150 (unsigned long long)v_block,
151 (unsigned long long)i_size_read(inode));
152 return -EIO;
153 }
154 rc = ocfs2_read_virt_blocks(inode, v_block, 1, &tmp, 0,
155 ocfs2_validate_quota_block);
156 if (rc)
157 mlog_errno(rc);
158
159 /* If ocfs2_read_virt_blocks() got us a new bh, pass it up. */
160 if (!rc && !*bh)
161 *bh = tmp;
162
163 return rc;
164}
165
132/* Check whether we understand format of quota files */ 166/* Check whether we understand format of quota files */
133static int ocfs2_local_check_quota_file(struct super_block *sb, int type) 167static int ocfs2_local_check_quota_file(struct super_block *sb, int type)
134{ 168{
@@ -972,10 +1006,8 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
972 } 1006 }
973 1007
974 /* Initialize chunk header */ 1008 /* Initialize chunk header */
975 down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
976 status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks, 1009 status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
977 &p_blkno, NULL, NULL); 1010 &p_blkno, NULL, NULL);
978 up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
979 if (status < 0) { 1011 if (status < 0) {
980 mlog_errno(status); 1012 mlog_errno(status);
981 goto out_trans; 1013 goto out_trans;
@@ -1003,10 +1035,8 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk(
1003 ocfs2_journal_dirty(handle, bh); 1035 ocfs2_journal_dirty(handle, bh);
1004 1036
1005 /* Initialize new block with structures */ 1037 /* Initialize new block with structures */
1006 down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
1007 status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks + 1, 1038 status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks + 1,
1008 &p_blkno, NULL, NULL); 1039 &p_blkno, NULL, NULL);
1009 up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
1010 if (status < 0) { 1040 if (status < 0) {
1011 mlog_errno(status); 1041 mlog_errno(status);
1012 goto out_trans; 1042 goto out_trans;
@@ -1103,10 +1133,8 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file(
1103 } 1133 }
1104 1134
1105 /* Get buffer from the just added block */ 1135 /* Get buffer from the just added block */
1106 down_read(&OCFS2_I(lqinode)->ip_alloc_sem);
1107 status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks, 1136 status = ocfs2_extent_map_get_blocks(lqinode, oinfo->dqi_blocks,
1108 &p_blkno, NULL, NULL); 1137 &p_blkno, NULL, NULL);
1109 up_read(&OCFS2_I(lqinode)->ip_alloc_sem);
1110 if (status < 0) { 1138 if (status < 0) {
1111 mlog_errno(status); 1139 mlog_errno(status);
1112 goto out; 1140 goto out;
@@ -1187,7 +1215,7 @@ static void olq_alloc_dquot(struct buffer_head *bh, void *private)
1187} 1215}
1188 1216
1189/* Create dquot in the local file for given id */ 1217/* Create dquot in the local file for given id */
1190static int ocfs2_create_local_dquot(struct dquot *dquot) 1218int ocfs2_create_local_dquot(struct dquot *dquot)
1191{ 1219{
1192 struct super_block *sb = dquot->dq_sb; 1220 struct super_block *sb = dquot->dq_sb;
1193 int type = dquot->dq_type; 1221 int type = dquot->dq_type;
@@ -1237,36 +1265,11 @@ out:
1237 return status; 1265 return status;
1238} 1266}
1239 1267
1240/* Create entry in local file for dquot, load data from the global file */ 1268/*
1241static int ocfs2_local_read_dquot(struct dquot *dquot) 1269 * Release dquot structure from local quota file. ocfs2_release_dquot() has
1242{ 1270 * already started a transaction and written all changes to global quota file
1243 int status; 1271 */
1244 1272int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot)
1245 mlog_entry("id=%u, type=%d\n", dquot->dq_id, dquot->dq_type);
1246
1247 status = ocfs2_global_read_dquot(dquot);
1248 if (status < 0) {
1249 mlog_errno(status);
1250 goto out_err;
1251 }
1252
1253 /* Now create entry in the local quota file */
1254 status = ocfs2_create_local_dquot(dquot);
1255 if (status < 0) {
1256 mlog_errno(status);
1257 goto out_err;
1258 }
1259 mlog_exit(0);
1260 return 0;
1261out_err:
1262 mlog_exit(status);
1263 return status;
1264}
1265
1266/* Release dquot structure from local quota file. ocfs2_release_dquot() has
1267 * already started a transaction and obtained exclusive lock for global
1268 * quota file. */
1269static int ocfs2_local_release_dquot(struct dquot *dquot)
1270{ 1273{
1271 int status; 1274 int status;
1272 int type = dquot->dq_type; 1275 int type = dquot->dq_type;
@@ -1274,15 +1277,6 @@ static int ocfs2_local_release_dquot(struct dquot *dquot)
1274 struct super_block *sb = dquot->dq_sb; 1277 struct super_block *sb = dquot->dq_sb;
1275 struct ocfs2_local_disk_chunk *dchunk; 1278 struct ocfs2_local_disk_chunk *dchunk;
1276 int offset; 1279 int offset;
1277 handle_t *handle = journal_current_handle();
1278
1279 BUG_ON(!handle);
1280 /* First write all local changes to global file */
1281 status = ocfs2_global_release_dquot(dquot);
1282 if (status < 0) {
1283 mlog_errno(status);
1284 goto out;
1285 }
1286 1280
1287 status = ocfs2_journal_access_dq(handle, 1281 status = ocfs2_journal_access_dq(handle,
1288 INODE_CACHE(sb_dqopt(sb)->files[type]), 1282 INODE_CACHE(sb_dqopt(sb)->files[type]),
@@ -1315,9 +1309,7 @@ static const struct quota_format_ops ocfs2_format_ops = {
1315 .read_file_info = ocfs2_local_read_info, 1309 .read_file_info = ocfs2_local_read_info,
1316 .write_file_info = ocfs2_global_write_info, 1310 .write_file_info = ocfs2_global_write_info,
1317 .free_file_info = ocfs2_local_free_info, 1311 .free_file_info = ocfs2_local_free_info,
1318 .read_dqblk = ocfs2_local_read_dquot,
1319 .commit_dqblk = ocfs2_local_write_dquot, 1312 .commit_dqblk = ocfs2_local_write_dquot,
1320 .release_dqblk = ocfs2_local_release_dquot,
1321}; 1313};
1322 1314
1323struct quota_format_type ocfs2_quota_format = { 1315struct quota_format_type ocfs2_quota_format = {