diff options
| author | Jan Kara <jack@suse.cz> | 2010-03-31 10:25:37 -0400 |
|---|---|---|
| committer | Jan Kara <jack@suse.cz> | 2010-05-21 13:30:47 -0400 |
| commit | fb8dd8d780140a3f0e9074831a59054fec6cc451 (patch) | |
| tree | 09e9f7bf157784fc6b0a7df71c1fbfe711349055 | |
| parent | ae4f6ef13417deaa49471c0e903914a3ef3be258 (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>
| -rw-r--r-- | fs/ocfs2/quota.h | 5 | ||||
| -rw-r--r-- | fs/ocfs2/quota_global.c | 307 | ||||
| -rw-r--r-- | fs/ocfs2/quota_local.c | 88 |
3 files changed, 214 insertions, 186 deletions
diff --git a/fs/ocfs2/quota.h b/fs/ocfs2/quota.h index c7623430ca3c..903ffa933d53 100644 --- a/fs/ocfs2/quota.h +++ b/fs/ocfs2/quota.h | |||
| @@ -104,10 +104,11 @@ static inline int ocfs2_global_release_dquot(struct dquot *dquot) | |||
| 104 | 104 | ||
| 105 | int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex); | 105 | int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex); |
| 106 | void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex); | 106 | void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex); |
| 107 | int ocfs2_read_quota_block(struct inode *inode, u64 v_block, | 107 | int ocfs2_validate_quota_block(struct super_block *sb, struct buffer_head *bh); |
| 108 | struct buffer_head **bh); | ||
| 109 | int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, | 108 | int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, |
| 110 | struct buffer_head **bh); | 109 | struct buffer_head **bh); |
| 110 | int ocfs2_create_local_dquot(struct dquot *dquot); | ||
| 111 | int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot); | ||
| 111 | 112 | ||
| 112 | extern const struct dquot_operations ocfs2_quota_operations; | 113 | extern const struct dquot_operations ocfs2_quota_operations; |
| 113 | extern struct quota_format_type ocfs2_quota_format; | 114 | extern struct quota_format_type ocfs2_quota_format; |
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index f391b11ea98c..1e3e0d5b3ae7 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c | |||
| @@ -28,6 +28,41 @@ | |||
| 28 | #include "buffer_head_io.h" | 28 | #include "buffer_head_io.h" |
| 29 | #include "quota.h" | 29 | #include "quota.h" |
| 30 | 30 | ||
| 31 | /* | ||
| 32 | * Locking of quotas with OCFS2 is rather complex. Here are rules that | ||
| 33 | * should be obeyed by all the functions: | ||
| 34 | * - any write of quota structure (either to local or global file) is protected | ||
| 35 | * by dqio_mutex or dquot->dq_lock. | ||
| 36 | * - any modification of global quota file holds inode cluster lock, i_mutex, | ||
| 37 | * and ip_alloc_sem of the global quota file (achieved by | ||
| 38 | * ocfs2_lock_global_qf). It also has to hold qinfo_lock. | ||
| 39 | * - an allocation of new blocks for local quota file is protected by | ||
| 40 | * its ip_alloc_sem | ||
| 41 | * | ||
| 42 | * A rough sketch of locking dependencies (lf = local file, gf = global file): | ||
| 43 | * Normal filesystem operation: | ||
| 44 | * start_trans -> dqio_mutex -> write to lf | ||
| 45 | * Syncing of local and global file: | ||
| 46 | * ocfs2_lock_global_qf -> start_trans -> dqio_mutex -> qinfo_lock -> | ||
| 47 | * write to gf | ||
| 48 | * -> write to lf | ||
| 49 | * Acquire dquot for the first time: | ||
| 50 | * dq_lock -> ocfs2_lock_global_qf -> qinfo_lock -> read from gf | ||
| 51 | * -> alloc space for gf | ||
| 52 | * -> start_trans -> qinfo_lock -> write to gf | ||
| 53 | * -> ip_alloc_sem of lf -> alloc space for lf | ||
| 54 | * -> write to lf | ||
| 55 | * Release last reference to dquot: | ||
| 56 | * dq_lock -> ocfs2_lock_global_qf -> start_trans -> qinfo_lock -> write to gf | ||
| 57 | * -> write to lf | ||
| 58 | * Note that all the above operations also hold the inode cluster lock of lf. | ||
| 59 | * Recovery: | ||
| 60 | * inode cluster lock of recovered lf | ||
| 61 | * -> read bitmaps -> ip_alloc_sem of lf | ||
| 62 | * -> ocfs2_lock_global_qf -> start_trans -> dqio_mutex -> qinfo_lock -> | ||
| 63 | * write to gf | ||
| 64 | */ | ||
| 65 | |||
| 31 | static struct workqueue_struct *ocfs2_quota_wq = NULL; | 66 | static struct workqueue_struct *ocfs2_quota_wq = NULL; |
| 32 | 67 | ||
| 33 | static void qsync_work_fn(struct work_struct *work); | 68 | static void qsync_work_fn(struct work_struct *work); |
| @@ -92,8 +127,7 @@ struct qtree_fmt_operations ocfs2_global_ops = { | |||
| 92 | .is_id = ocfs2_global_is_id, | 127 | .is_id = ocfs2_global_is_id, |
| 93 | }; | 128 | }; |
| 94 | 129 | ||
| 95 | static int ocfs2_validate_quota_block(struct super_block *sb, | 130 | int ocfs2_validate_quota_block(struct super_block *sb, struct buffer_head *bh) |
| 96 | struct buffer_head *bh) | ||
| 97 | { | 131 | { |
| 98 | struct ocfs2_disk_dqtrailer *dqt = | 132 | struct ocfs2_disk_dqtrailer *dqt = |
| 99 | ocfs2_block_dqtrailer(sb->s_blocksize, bh->b_data); | 133 | ocfs2_block_dqtrailer(sb->s_blocksize, bh->b_data); |
| @@ -111,33 +145,6 @@ static int ocfs2_validate_quota_block(struct super_block *sb, | |||
| 111 | return ocfs2_validate_meta_ecc(sb, bh->b_data, &dqt->dq_check); | 145 | return ocfs2_validate_meta_ecc(sb, bh->b_data, &dqt->dq_check); |
| 112 | } | 146 | } |
| 113 | 147 | ||
| 114 | int ocfs2_read_quota_block(struct inode *inode, u64 v_block, | ||
| 115 | struct buffer_head **bh) | ||
| 116 | { | ||
| 117 | int rc = 0; | ||
| 118 | struct buffer_head *tmp = *bh; | ||
| 119 | |||
| 120 | if (i_size_read(inode) >> inode->i_sb->s_blocksize_bits <= v_block) { | ||
| 121 | ocfs2_error(inode->i_sb, | ||
| 122 | "Quota file %llu is probably corrupted! Requested " | ||
| 123 | "to read block %Lu but file has size only %Lu\n", | ||
| 124 | (unsigned long long)OCFS2_I(inode)->ip_blkno, | ||
| 125 | (unsigned long long)v_block, | ||
| 126 | (unsigned long long)i_size_read(inode)); | ||
| 127 | return -EIO; | ||
| 128 | } | ||
| 129 | rc = ocfs2_read_virt_blocks(inode, v_block, 1, &tmp, 0, | ||
| 130 | ocfs2_validate_quota_block); | ||
| 131 | if (rc) | ||
| 132 | mlog_errno(rc); | ||
| 133 | |||
| 134 | /* If ocfs2_read_virt_blocks() got us a new bh, pass it up. */ | ||
| 135 | if (!rc && !*bh) | ||
| 136 | *bh = tmp; | ||
| 137 | |||
| 138 | return rc; | ||
| 139 | } | ||
| 140 | |||
| 141 | int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, | 148 | int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, |
| 142 | struct buffer_head **bhp) | 149 | struct buffer_head **bhp) |
| 143 | { | 150 | { |
| @@ -151,27 +158,6 @@ int ocfs2_read_quota_phys_block(struct inode *inode, u64 p_block, | |||
| 151 | return rc; | 158 | return rc; |
| 152 | } | 159 | } |
| 153 | 160 | ||
| 154 | static int ocfs2_get_quota_block(struct inode *inode, int block, | ||
| 155 | struct buffer_head **bh) | ||
| 156 | { | ||
| 157 | u64 pblock, pcount; | ||
| 158 | int err; | ||
| 159 | |||
| 160 | down_read(&OCFS2_I(inode)->ip_alloc_sem); | ||
| 161 | err = ocfs2_extent_map_get_blocks(inode, block, &pblock, &pcount, NULL); | ||
| 162 | up_read(&OCFS2_I(inode)->ip_alloc_sem); | ||
| 163 | if (err) { | ||
| 164 | mlog_errno(err); | ||
| 165 | return err; | ||
| 166 | } | ||
| 167 | *bh = sb_getblk(inode->i_sb, pblock); | ||
| 168 | if (!*bh) { | ||
| 169 | err = -EIO; | ||
| 170 | mlog_errno(err); | ||
| 171 | } | ||
| 172 | return err; | ||
| 173 | } | ||
| 174 | |||
| 175 | /* Read data from global quotafile - avoid pagecache and such because we cannot | 161 | /* Read data from global quotafile - avoid pagecache and such because we cannot |
| 176 | * afford acquiring the locks... We use quota cluster lock to serialize | 162 | * afford acquiring the locks... We use quota cluster lock to serialize |
| 177 | * operations. Caller is responsible for acquiring it. */ | 163 | * operations. Caller is responsible for acquiring it. */ |
| @@ -186,6 +172,7 @@ ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data, | |||
| 186 | int err = 0; | 172 | int err = 0; |
| 187 | struct buffer_head *bh; | 173 | struct buffer_head *bh; |
| 188 | size_t toread, tocopy; | 174 | size_t toread, tocopy; |
| 175 | u64 pblock = 0, pcount = 0; | ||
| 189 | 176 | ||
| 190 | if (off > i_size) | 177 | if (off > i_size) |
| 191 | return 0; | 178 | return 0; |
| @@ -194,8 +181,19 @@ ssize_t ocfs2_quota_read(struct super_block *sb, int type, char *data, | |||
| 194 | toread = len; | 181 | toread = len; |
| 195 | while (toread > 0) { | 182 | while (toread > 0) { |
| 196 | tocopy = min_t(size_t, (sb->s_blocksize - offset), toread); | 183 | tocopy = min_t(size_t, (sb->s_blocksize - offset), toread); |
| 184 | if (!pcount) { | ||
| 185 | err = ocfs2_extent_map_get_blocks(gqinode, blk, &pblock, | ||
| 186 | &pcount, NULL); | ||
| 187 | if (err) { | ||
| 188 | mlog_errno(err); | ||
| 189 | return err; | ||
| 190 | } | ||
| 191 | } else { | ||
| 192 | pcount--; | ||
| 193 | pblock++; | ||
| 194 | } | ||
| 197 | bh = NULL; | 195 | bh = NULL; |
| 198 | err = ocfs2_read_quota_block(gqinode, blk, &bh); | 196 | err = ocfs2_read_quota_phys_block(gqinode, pblock, &bh); |
| 199 | if (err) { | 197 | if (err) { |
| 200 | mlog_errno(err); | 198 | mlog_errno(err); |
| 201 | return err; | 199 | return err; |
| @@ -223,6 +221,7 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, | |||
| 223 | int err = 0, new = 0, ja_type; | 221 | int err = 0, new = 0, ja_type; |
| 224 | struct buffer_head *bh = NULL; | 222 | struct buffer_head *bh = NULL; |
| 225 | handle_t *handle = journal_current_handle(); | 223 | handle_t *handle = journal_current_handle(); |
| 224 | u64 pblock, pcount; | ||
| 226 | 225 | ||
| 227 | if (!handle) { | 226 | if (!handle) { |
| 228 | mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled " | 227 | mlog(ML_ERROR, "Quota write (off=%llu, len=%llu) cancelled " |
| @@ -235,12 +234,11 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, | |||
| 235 | len = sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE - offset; | 234 | len = sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE - offset; |
| 236 | } | 235 | } |
| 237 | 236 | ||
| 238 | mutex_lock_nested(&gqinode->i_mutex, I_MUTEX_QUOTA); | ||
| 239 | if (gqinode->i_size < off + len) { | 237 | if (gqinode->i_size < off + len) { |
| 240 | loff_t rounded_end = | 238 | loff_t rounded_end = |
| 241 | ocfs2_align_bytes_to_blocks(sb, off + len); | 239 | ocfs2_align_bytes_to_blocks(sb, off + len); |
| 242 | 240 | ||
| 243 | /* Space is already allocated in ocfs2_global_read_dquot() */ | 241 | /* Space is already allocated in ocfs2_acquire_dquot() */ |
| 244 | err = ocfs2_simple_size_update(gqinode, | 242 | err = ocfs2_simple_size_update(gqinode, |
| 245 | oinfo->dqi_gqi_bh, | 243 | oinfo->dqi_gqi_bh, |
| 246 | rounded_end); | 244 | rounded_end); |
| @@ -248,13 +246,20 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, | |||
| 248 | goto out; | 246 | goto out; |
| 249 | new = 1; | 247 | new = 1; |
| 250 | } | 248 | } |
| 249 | err = ocfs2_extent_map_get_blocks(gqinode, blk, &pblock, &pcount, NULL); | ||
| 250 | if (err) { | ||
| 251 | mlog_errno(err); | ||
| 252 | goto out; | ||
| 253 | } | ||
| 251 | /* Not rewriting whole block? */ | 254 | /* Not rewriting whole block? */ |
| 252 | if ((offset || len < sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) && | 255 | if ((offset || len < sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE) && |
| 253 | !new) { | 256 | !new) { |
| 254 | err = ocfs2_read_quota_block(gqinode, blk, &bh); | 257 | err = ocfs2_read_quota_phys_block(gqinode, pblock, &bh); |
| 255 | ja_type = OCFS2_JOURNAL_ACCESS_WRITE; | 258 | ja_type = OCFS2_JOURNAL_ACCESS_WRITE; |
| 256 | } else { | 259 | } else { |
| 257 | err = ocfs2_get_quota_block(gqinode, blk, &bh); | 260 | bh = sb_getblk(sb, pblock); |
| 261 | if (!bh) | ||
| 262 | err = -ENOMEM; | ||
| 258 | ja_type = OCFS2_JOURNAL_ACCESS_CREATE; | 263 | ja_type = OCFS2_JOURNAL_ACCESS_CREATE; |
| 259 | } | 264 | } |
| 260 | if (err) { | 265 | if (err) { |
| @@ -279,13 +284,11 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, | |||
| 279 | brelse(bh); | 284 | brelse(bh); |
| 280 | out: | 285 | out: |
| 281 | if (err) { | 286 | if (err) { |
| 282 | mutex_unlock(&gqinode->i_mutex); | ||
| 283 | mlog_errno(err); | 287 | mlog_errno(err); |
| 284 | return err; | 288 | return err; |
| 285 | } | 289 | } |
| 286 | gqinode->i_version++; | 290 | gqinode->i_version++; |
| 287 | ocfs2_mark_inode_dirty(handle, gqinode, oinfo->dqi_gqi_bh); | 291 | ocfs2_mark_inode_dirty(handle, gqinode, oinfo->dqi_gqi_bh); |
| 288 | mutex_unlock(&gqinode->i_mutex); | ||
| 289 | return len; | 292 | return len; |
| 290 | } | 293 | } |
| 291 | 294 | ||
| @@ -303,11 +306,23 @@ int ocfs2_lock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) | |||
| 303 | else | 306 | else |
| 304 | WARN_ON(bh != oinfo->dqi_gqi_bh); | 307 | WARN_ON(bh != oinfo->dqi_gqi_bh); |
| 305 | spin_unlock(&dq_data_lock); | 308 | spin_unlock(&dq_data_lock); |
| 309 | if (ex) { | ||
| 310 | mutex_lock(&oinfo->dqi_gqinode->i_mutex); | ||
| 311 | down_write(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem); | ||
| 312 | } else { | ||
| 313 | down_read(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem); | ||
| 314 | } | ||
| 306 | return 0; | 315 | return 0; |
| 307 | } | 316 | } |
| 308 | 317 | ||
| 309 | void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) | 318 | void ocfs2_unlock_global_qf(struct ocfs2_mem_dqinfo *oinfo, int ex) |
| 310 | { | 319 | { |
| 320 | if (ex) { | ||
| 321 | up_write(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem); | ||
| 322 | mutex_unlock(&oinfo->dqi_gqinode->i_mutex); | ||
| 323 | } else { | ||
| 324 | up_read(&OCFS2_I(oinfo->dqi_gqinode)->ip_alloc_sem); | ||
| 325 | } | ||
| 311 | ocfs2_inode_unlock(oinfo->dqi_gqinode, ex); | 326 | ocfs2_inode_unlock(oinfo->dqi_gqinode, ex); |
| 312 | brelse(oinfo->dqi_gqi_bh); | 327 | brelse(oinfo->dqi_gqi_bh); |
| 313 | spin_lock(&dq_data_lock); | 328 | spin_lock(&dq_data_lock); |
| @@ -458,75 +473,6 @@ static int ocfs2_calc_global_qinit_credits(struct super_block *sb, int type) | |||
| 458 | OCFS2_QUOTA_BLOCK_UPDATE_CREDITS; | 473 | OCFS2_QUOTA_BLOCK_UPDATE_CREDITS; |
| 459 | } | 474 | } |
| 460 | 475 | ||
| 461 | /* Read in information from global quota file and acquire a reference to it. | ||
| 462 | * dquot_acquire() has already started the transaction and locked quota file */ | ||
| 463 | int ocfs2_global_read_dquot(struct dquot *dquot) | ||
| 464 | { | ||
| 465 | int err, err2, ex = 0; | ||
| 466 | struct super_block *sb = dquot->dq_sb; | ||
| 467 | int type = dquot->dq_type; | ||
| 468 | struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv; | ||
| 469 | struct ocfs2_super *osb = OCFS2_SB(sb); | ||
| 470 | struct inode *gqinode = info->dqi_gqinode; | ||
| 471 | int need_alloc = ocfs2_global_qinit_alloc(sb, type); | ||
| 472 | handle_t *handle = NULL; | ||
| 473 | |||
| 474 | err = ocfs2_qinfo_lock(info, 0); | ||
| 475 | if (err < 0) | ||
| 476 | goto out; | ||
| 477 | err = qtree_read_dquot(&info->dqi_gi, dquot); | ||
| 478 | if (err < 0) | ||
| 479 | goto out_qlock; | ||
| 480 | OCFS2_DQUOT(dquot)->dq_use_count++; | ||
| 481 | OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; | ||
| 482 | OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; | ||
| 483 | ocfs2_qinfo_unlock(info, 0); | ||
| 484 | |||
| 485 | if (!dquot->dq_off) { /* No real quota entry? */ | ||
| 486 | ex = 1; | ||
| 487 | /* | ||
| 488 | * Add blocks to quota file before we start a transaction since | ||
| 489 | * locking allocators ranks above a transaction start | ||
| 490 | */ | ||
| 491 | WARN_ON(journal_current_handle()); | ||
| 492 | down_write(&OCFS2_I(gqinode)->ip_alloc_sem); | ||
| 493 | err = ocfs2_extend_no_holes(gqinode, | ||
| 494 | gqinode->i_size + (need_alloc << sb->s_blocksize_bits), | ||
| 495 | gqinode->i_size); | ||
| 496 | up_write(&OCFS2_I(gqinode)->ip_alloc_sem); | ||
| 497 | if (err < 0) | ||
| 498 | goto out; | ||
| 499 | } | ||
| 500 | |||
| 501 | handle = ocfs2_start_trans(osb, | ||
| 502 | ocfs2_calc_global_qinit_credits(sb, type)); | ||
| 503 | if (IS_ERR(handle)) { | ||
| 504 | err = PTR_ERR(handle); | ||
| 505 | goto out; | ||
| 506 | } | ||
| 507 | err = ocfs2_qinfo_lock(info, ex); | ||
| 508 | if (err < 0) | ||
| 509 | goto out_trans; | ||
| 510 | err = qtree_write_dquot(&info->dqi_gi, dquot); | ||
| 511 | if (ex && info_dirty(sb_dqinfo(dquot->dq_sb, dquot->dq_type))) { | ||
| 512 | err2 = __ocfs2_global_write_info(dquot->dq_sb, dquot->dq_type); | ||
| 513 | if (!err) | ||
| 514 | err = err2; | ||
| 515 | } | ||
| 516 | out_qlock: | ||
| 517 | if (ex) | ||
| 518 | ocfs2_qinfo_unlock(info, 1); | ||
| 519 | else | ||
| 520 | ocfs2_qinfo_unlock(info, 0); | ||
| 521 | out_trans: | ||
| 522 | if (handle) | ||
| 523 | ocfs2_commit_trans(osb, handle); | ||
| 524 | out: | ||
| 525 | if (err < 0) | ||
| 526 | mlog_errno(err); | ||
| 527 | return err; | ||
| 528 | } | ||
| 529 | |||
| 530 | /* Sync local information about quota modifications with global quota file. | 476 | /* Sync local information about quota modifications with global quota file. |
| 531 | * Caller must have started the transaction and obtained exclusive lock for | 477 | * Caller must have started the transaction and obtained exclusive lock for |
| 532 | * global quota file inode */ | 478 | * global quota file inode */ |
| @@ -742,6 +688,10 @@ static int ocfs2_release_dquot(struct dquot *dquot) | |||
| 742 | 688 | ||
| 743 | mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type); | 689 | mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type); |
| 744 | 690 | ||
| 691 | mutex_lock(&dquot->dq_lock); | ||
| 692 | /* Check whether we are not racing with some other dqget() */ | ||
| 693 | if (atomic_read(&dquot->dq_count) > 1) | ||
| 694 | goto out; | ||
| 745 | status = ocfs2_lock_global_qf(oinfo, 1); | 695 | status = ocfs2_lock_global_qf(oinfo, 1); |
| 746 | if (status < 0) | 696 | if (status < 0) |
| 747 | goto out; | 697 | goto out; |
| @@ -752,30 +702,113 @@ static int ocfs2_release_dquot(struct dquot *dquot) | |||
| 752 | mlog_errno(status); | 702 | mlog_errno(status); |
| 753 | goto out_ilock; | 703 | goto out_ilock; |
| 754 | } | 704 | } |
| 755 | status = dquot_release(dquot); | 705 | |
| 706 | status = ocfs2_global_release_dquot(dquot); | ||
| 707 | if (status < 0) { | ||
| 708 | mlog_errno(status); | ||
| 709 | goto out_trans; | ||
| 710 | } | ||
| 711 | status = ocfs2_local_release_dquot(handle, dquot); | ||
| 712 | /* | ||
| 713 | * If we fail here, we cannot do much as global structure is | ||
| 714 | * already released. So just complain... | ||
| 715 | */ | ||
| 716 | if (status < 0) | ||
| 717 | mlog_errno(status); | ||
| 718 | clear_bit(DQ_ACTIVE_B, &dquot->dq_flags); | ||
| 719 | out_trans: | ||
| 756 | ocfs2_commit_trans(osb, handle); | 720 | ocfs2_commit_trans(osb, handle); |
| 757 | out_ilock: | 721 | out_ilock: |
| 758 | ocfs2_unlock_global_qf(oinfo, 1); | 722 | ocfs2_unlock_global_qf(oinfo, 1); |
| 759 | out: | 723 | out: |
| 724 | mutex_unlock(&dquot->dq_lock); | ||
| 760 | mlog_exit(status); | 725 | mlog_exit(status); |
| 761 | return status; | 726 | return status; |
| 762 | } | 727 | } |
| 763 | 728 | ||
| 729 | /* | ||
| 730 | * Read global dquot structure from disk or create it if it does | ||
| 731 | * not exist. Also update use count of the global structure and | ||
| 732 | * create structure in node-local quota file. | ||
| 733 | */ | ||
| 764 | static int ocfs2_acquire_dquot(struct dquot *dquot) | 734 | static int ocfs2_acquire_dquot(struct dquot *dquot) |
| 765 | { | 735 | { |
| 766 | struct ocfs2_mem_dqinfo *oinfo = | 736 | int status = 0, err; |
| 767 | sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv; | 737 | int ex = 0; |
| 768 | int status = 0; | 738 | struct super_block *sb = dquot->dq_sb; |
| 739 | struct ocfs2_super *osb = OCFS2_SB(sb); | ||
| 740 | int type = dquot->dq_type; | ||
| 741 | struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv; | ||
| 742 | struct inode *gqinode = info->dqi_gqinode; | ||
| 743 | int need_alloc = ocfs2_global_qinit_alloc(sb, type); | ||
| 744 | handle_t *handle; | ||
| 769 | 745 | ||
| 770 | mlog_entry("id=%u, type=%d", dquot->dq_id, dquot->dq_type); | 746 | mlog_entry("id=%u, type=%d", dquot->dq_id, type); |
| 771 | /* We need an exclusive lock, because we're going to update use count | 747 | mutex_lock(&dquot->dq_lock); |
| 772 | * and instantiate possibly new dquot structure */ | 748 | /* |
| 773 | status = ocfs2_lock_global_qf(oinfo, 1); | 749 | * We need an exclusive lock, because we're going to update use count |
| 750 | * and instantiate possibly new dquot structure | ||
| 751 | */ | ||
| 752 | status = ocfs2_lock_global_qf(info, 1); | ||
| 774 | if (status < 0) | 753 | if (status < 0) |
| 775 | goto out; | 754 | goto out; |
| 776 | status = dquot_acquire(dquot); | 755 | if (!test_bit(DQ_READ_B, &dquot->dq_flags)) { |
| 777 | ocfs2_unlock_global_qf(oinfo, 1); | 756 | status = ocfs2_qinfo_lock(info, 0); |
| 757 | if (status < 0) | ||
| 758 | goto out_dq; | ||
| 759 | status = qtree_read_dquot(&info->dqi_gi, dquot); | ||
| 760 | ocfs2_qinfo_unlock(info, 0); | ||
| 761 | if (status < 0) | ||
| 762 | goto out_dq; | ||
| 763 | } | ||
| 764 | set_bit(DQ_READ_B, &dquot->dq_flags); | ||
| 765 | |||
| 766 | OCFS2_DQUOT(dquot)->dq_use_count++; | ||
| 767 | OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace; | ||
| 768 | OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; | ||
| 769 | if (!dquot->dq_off) { /* No real quota entry? */ | ||
| 770 | ex = 1; | ||
| 771 | /* | ||
| 772 | * Add blocks to quota file before we start a transaction since | ||
| 773 | * locking allocators ranks above a transaction start | ||
| 774 | */ | ||
| 775 | WARN_ON(journal_current_handle()); | ||
| 776 | status = ocfs2_extend_no_holes(gqinode, | ||
| 777 | gqinode->i_size + (need_alloc << sb->s_blocksize_bits), | ||
| 778 | gqinode->i_size); | ||
| 779 | if (status < 0) | ||
| 780 | goto out_dq; | ||
| 781 | } | ||
| 782 | |||
| 783 | handle = ocfs2_start_trans(osb, | ||
| 784 | ocfs2_calc_global_qinit_credits(sb, type)); | ||
| 785 | if (IS_ERR(handle)) { | ||
| 786 | status = PTR_ERR(handle); | ||
| 787 | goto out_dq; | ||
| 788 | } | ||
| 789 | status = ocfs2_qinfo_lock(info, ex); | ||
| 790 | if (status < 0) | ||
| 791 | goto out_trans; | ||
| 792 | status = qtree_write_dquot(&info->dqi_gi, dquot); | ||
| 793 | if (ex && info_dirty(sb_dqinfo(sb, type))) { | ||
| 794 | err = __ocfs2_global_write_info(sb, type); | ||
| 795 | if (!status) | ||
| 796 | status = err; | ||
| 797 | } | ||
| 798 | ocfs2_qinfo_unlock(info, ex); | ||
| 799 | out_trans: | ||
| 800 | ocfs2_commit_trans(osb, handle); | ||
| 801 | out_dq: | ||
| 802 | ocfs2_unlock_global_qf(info, 1); | ||
| 803 | if (status < 0) | ||
| 804 | goto out; | ||
| 805 | |||
| 806 | status = ocfs2_create_local_dquot(dquot); | ||
| 807 | if (status < 0) | ||
| 808 | goto out; | ||
| 809 | set_bit(DQ_ACTIVE_B, &dquot->dq_flags); | ||
| 778 | out: | 810 | out: |
| 811 | mutex_unlock(&dquot->dq_lock); | ||
| 779 | mlog_exit(status); | 812 | mlog_exit(status); |
| 780 | return status; | 813 | return status; |
| 781 | } | 814 | } |
| @@ -820,7 +853,9 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot) | |||
| 820 | mlog_errno(status); | 853 | mlog_errno(status); |
| 821 | goto out_ilock; | 854 | goto out_ilock; |
| 822 | } | 855 | } |
| 856 | mutex_lock(&sb_dqopt(sb)->dqio_mutex); | ||
| 823 | status = ocfs2_sync_dquot(dquot); | 857 | status = ocfs2_sync_dquot(dquot); |
| 858 | mutex_unlock(&sb_dqopt(sb)->dqio_mutex); | ||
| 824 | if (status < 0) { | 859 | if (status < 0) { |
| 825 | mlog_errno(status); | 860 | mlog_errno(status); |
| 826 | goto out_trans; | 861 | goto out_trans; |
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 */ |
| 27 | static inline unsigned int ol_quota_entries_per_block(struct super_block *sb) | 28 | static 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 | */ | ||
| 139 | static 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 */ |
| 133 | static int ocfs2_local_check_quota_file(struct super_block *sb, int type) | 167 | static 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 */ |
| 1190 | static int ocfs2_create_local_dquot(struct dquot *dquot) | 1218 | int 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 | /* |
| 1241 | static 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 | 1272 | int 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; | ||
| 1261 | out_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. */ | ||
| 1269 | static 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 | ||
| 1323 | struct quota_format_type ocfs2_quota_format = { | 1315 | struct quota_format_type ocfs2_quota_format = { |
