aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2/quota_local.c
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2008-10-20 17:50:38 -0400
committerMark Fasheh <mfasheh@suse.com>2009-01-05 11:40:24 -0500
commit2205363dce7447b8e85f1ead14387664c1a98753 (patch)
tree729b2716f2e31bda2e035a11cc39aa5472dff2c4 /fs/ocfs2/quota_local.c
parent171bf93ce11f4c9929fdce6ce63df8da2f3c4475 (diff)
ocfs2: Implement quota recovery
Implement functions for recovery after a crash. Functions just read local quota file and sync info to global quota file. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/ocfs2/quota_local.c')
-rw-r--r--fs/ocfs2/quota_local.c425
1 files changed, 417 insertions, 8 deletions
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index 40e82b483136..b98562174cd0 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -49,14 +49,25 @@ static unsigned int ol_quota_chunk_block(struct super_block *sb, int c)
49 return 1 + (ol_chunk_blocks(sb) + 1) * c; 49 return 1 + (ol_chunk_blocks(sb) + 1) * c;
50} 50}
51 51
52/* Offset of the dquot structure in the quota file */ 52static unsigned int ol_dqblk_block(struct super_block *sb, int c, int off)
53static loff_t ol_dqblk_off(struct super_block *sb, int c, int off) 53{
54 int epb = ol_quota_entries_per_block(sb);
55
56 return ol_quota_chunk_block(sb, c) + 1 + off / epb;
57}
58
59static unsigned int ol_dqblk_block_off(struct super_block *sb, int c, int off)
54{ 60{
55 int epb = ol_quota_entries_per_block(sb); 61 int epb = ol_quota_entries_per_block(sb);
56 62
57 return ((ol_quota_chunk_block(sb, c) + 1 + off / epb) 63 return (off % epb) * sizeof(struct ocfs2_local_disk_dqblk);
58 << sb->s_blocksize_bits) + 64}
59 (off % epb) * sizeof(struct ocfs2_local_disk_dqblk); 65
66/* Offset of the dquot structure in the quota file */
67static loff_t ol_dqblk_off(struct super_block *sb, int c, int off)
68{
69 return (ol_dqblk_block(sb, c, off) << sb->s_blocksize_bits) +
70 ol_dqblk_block_off(sb, c, off);
60} 71}
61 72
62/* Compute block number from given offset */ 73/* Compute block number from given offset */
@@ -253,6 +264,379 @@ static void olq_update_info(struct buffer_head *bh, void *private)
253 spin_unlock(&dq_data_lock); 264 spin_unlock(&dq_data_lock);
254} 265}
255 266
267static int ocfs2_add_recovery_chunk(struct super_block *sb,
268 struct ocfs2_local_disk_chunk *dchunk,
269 int chunk,
270 struct list_head *head)
271{
272 struct ocfs2_recovery_chunk *rc;
273
274 rc = kmalloc(sizeof(struct ocfs2_recovery_chunk), GFP_NOFS);
275 if (!rc)
276 return -ENOMEM;
277 rc->rc_chunk = chunk;
278 rc->rc_bitmap = kmalloc(sb->s_blocksize, GFP_NOFS);
279 if (!rc->rc_bitmap) {
280 kfree(rc);
281 return -ENOMEM;
282 }
283 memcpy(rc->rc_bitmap, dchunk->dqc_bitmap,
284 (ol_chunk_entries(sb) + 7) >> 3);
285 list_add_tail(&rc->rc_list, head);
286 return 0;
287}
288
289static void free_recovery_list(struct list_head *head)
290{
291 struct ocfs2_recovery_chunk *next;
292 struct ocfs2_recovery_chunk *rchunk;
293
294 list_for_each_entry_safe(rchunk, next, head, rc_list) {
295 list_del(&rchunk->rc_list);
296 kfree(rchunk->rc_bitmap);
297 kfree(rchunk);
298 }
299}
300
301void ocfs2_free_quota_recovery(struct ocfs2_quota_recovery *rec)
302{
303 int type;
304
305 for (type = 0; type < MAXQUOTAS; type++)
306 free_recovery_list(&(rec->r_list[type]));
307 kfree(rec);
308}
309
310/* Load entries in our quota file we have to recover*/
311static int ocfs2_recovery_load_quota(struct inode *lqinode,
312 struct ocfs2_local_disk_dqinfo *ldinfo,
313 int type,
314 struct list_head *head)
315{
316 struct super_block *sb = lqinode->i_sb;
317 struct buffer_head *hbh;
318 struct ocfs2_local_disk_chunk *dchunk;
319 int i, chunks = le32_to_cpu(ldinfo->dqi_chunks);
320 int status = 0;
321
322 for (i = 0; i < chunks; i++) {
323 hbh = ocfs2_read_quota_block(lqinode,
324 ol_quota_chunk_block(sb, i),
325 &status);
326 if (!hbh) {
327 mlog_errno(status);
328 break;
329 }
330 dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
331 if (le32_to_cpu(dchunk->dqc_free) < ol_chunk_entries(sb))
332 status = ocfs2_add_recovery_chunk(sb, dchunk, i, head);
333 brelse(hbh);
334 if (status < 0)
335 break;
336 }
337 if (status < 0)
338 free_recovery_list(head);
339 return status;
340}
341
342static struct ocfs2_quota_recovery *ocfs2_alloc_quota_recovery(void)
343{
344 int type;
345 struct ocfs2_quota_recovery *rec;
346
347 rec = kmalloc(sizeof(struct ocfs2_quota_recovery), GFP_NOFS);
348 if (!rec)
349 return NULL;
350 for (type = 0; type < MAXQUOTAS; type++)
351 INIT_LIST_HEAD(&(rec->r_list[type]));
352 return rec;
353}
354
355/* Load information we need for quota recovery into memory */
356struct ocfs2_quota_recovery *ocfs2_begin_quota_recovery(
357 struct ocfs2_super *osb,
358 int slot_num)
359{
360 unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
361 OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
362 unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
363 LOCAL_GROUP_QUOTA_SYSTEM_INODE };
364 struct super_block *sb = osb->sb;
365 struct ocfs2_local_disk_dqinfo *ldinfo;
366 struct inode *lqinode;
367 struct buffer_head *bh;
368 int type;
369 int status = 0;
370 struct ocfs2_quota_recovery *rec;
371
372 mlog(ML_NOTICE, "Beginning quota recovery in slot %u\n", slot_num);
373 rec = ocfs2_alloc_quota_recovery();
374 if (!rec)
375 return ERR_PTR(-ENOMEM);
376 /* First init... */
377
378 for (type = 0; type < MAXQUOTAS; type++) {
379 if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type]))
380 continue;
381 /* At this point, journal of the slot is already replayed so
382 * we can trust metadata and data of the quota file */
383 lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
384 if (!lqinode) {
385 status = -ENOENT;
386 goto out;
387 }
388 status = ocfs2_inode_lock_full(lqinode, NULL, 1,
389 OCFS2_META_LOCK_RECOVERY);
390 if (status < 0) {
391 mlog_errno(status);
392 goto out_put;
393 }
394 /* Now read local header */
395 bh = ocfs2_read_quota_block(lqinode, 0, &status);
396 if (!bh) {
397 mlog_errno(status);
398 mlog(ML_ERROR, "failed to read quota file info header "
399 "(slot=%d type=%d)\n", slot_num, type);
400 goto out_lock;
401 }
402 ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
403 OCFS2_LOCAL_INFO_OFF);
404 status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
405 &rec->r_list[type]);
406 brelse(bh);
407out_lock:
408 ocfs2_inode_unlock(lqinode, 1);
409out_put:
410 iput(lqinode);
411 if (status < 0)
412 break;
413 }
414out:
415 if (status < 0) {
416 ocfs2_free_quota_recovery(rec);
417 rec = ERR_PTR(status);
418 }
419 return rec;
420}
421
422/* Sync changes in local quota file into global quota file and
423 * reinitialize local quota file.
424 * The function expects local quota file to be already locked and
425 * dqonoff_mutex locked. */
426static int ocfs2_recover_local_quota_file(struct inode *lqinode,
427 int type,
428 struct ocfs2_quota_recovery *rec)
429{
430 struct super_block *sb = lqinode->i_sb;
431 struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
432 struct ocfs2_local_disk_chunk *dchunk;
433 struct ocfs2_local_disk_dqblk *dqblk;
434 struct dquot *dquot;
435 handle_t *handle;
436 struct buffer_head *hbh = NULL, *qbh = NULL;
437 int status = 0;
438 int bit, chunk;
439 struct ocfs2_recovery_chunk *rchunk, *next;
440 qsize_t spacechange, inodechange;
441
442 mlog_entry("ino=%lu type=%u", (unsigned long)lqinode->i_ino, type);
443
444 status = ocfs2_lock_global_qf(oinfo, 1);
445 if (status < 0)
446 goto out;
447
448 list_for_each_entry_safe(rchunk, next, &(rec->r_list[type]), rc_list) {
449 chunk = rchunk->rc_chunk;
450 hbh = ocfs2_read_quota_block(lqinode,
451 ol_quota_chunk_block(sb, chunk),
452 &status);
453 if (!hbh) {
454 mlog_errno(status);
455 break;
456 }
457 dchunk = (struct ocfs2_local_disk_chunk *)hbh->b_data;
458 for_each_bit(bit, rchunk->rc_bitmap, ol_chunk_entries(sb)) {
459 qbh = ocfs2_read_quota_block(lqinode,
460 ol_dqblk_block(sb, chunk, bit),
461 &status);
462 if (!qbh) {
463 mlog_errno(status);
464 break;
465 }
466 dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
467 ol_dqblk_block_off(sb, chunk, bit));
468 dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
469 if (!dquot) {
470 status = -EIO;
471 mlog(ML_ERROR, "Failed to get quota structure "
472 "for id %u, type %d. Cannot finish quota "
473 "file recovery.\n",
474 (unsigned)le64_to_cpu(dqblk->dqb_id),
475 type);
476 goto out_put_bh;
477 }
478 handle = ocfs2_start_trans(OCFS2_SB(sb),
479 OCFS2_QSYNC_CREDITS);
480 if (IS_ERR(handle)) {
481 status = PTR_ERR(handle);
482 mlog_errno(status);
483 goto out_put_dquot;
484 }
485 mutex_lock(&sb_dqopt(sb)->dqio_mutex);
486 spin_lock(&dq_data_lock);
487 /* Add usage from quota entry into quota changes
488 * of our node. Auxiliary variables are important
489 * due to signedness */
490 spacechange = le64_to_cpu(dqblk->dqb_spacemod);
491 inodechange = le64_to_cpu(dqblk->dqb_inodemod);
492 dquot->dq_dqb.dqb_curspace += spacechange;
493 dquot->dq_dqb.dqb_curinodes += inodechange;
494 spin_unlock(&dq_data_lock);
495 /* We want to drop reference held by the crashed
496 * node. Since we have our own reference we know
497 * global structure actually won't be freed. */
498 status = ocfs2_global_release_dquot(dquot);
499 if (status < 0) {
500 mlog_errno(status);
501 goto out_commit;
502 }
503 /* Release local quota file entry */
504 status = ocfs2_journal_access(handle, lqinode,
505 qbh, OCFS2_JOURNAL_ACCESS_WRITE);
506 if (status < 0) {
507 mlog_errno(status);
508 goto out_commit;
509 }
510 lock_buffer(qbh);
511 WARN_ON(!ocfs2_test_bit(bit, dchunk->dqc_bitmap));
512 ocfs2_clear_bit(bit, dchunk->dqc_bitmap);
513 le32_add_cpu(&dchunk->dqc_free, 1);
514 unlock_buffer(qbh);
515 status = ocfs2_journal_dirty(handle, qbh);
516 if (status < 0)
517 mlog_errno(status);
518out_commit:
519 mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
520 ocfs2_commit_trans(OCFS2_SB(sb), handle);
521out_put_dquot:
522 dqput(dquot);
523out_put_bh:
524 brelse(qbh);
525 if (status < 0)
526 break;
527 }
528 brelse(hbh);
529 list_del(&rchunk->rc_list);
530 kfree(rchunk->rc_bitmap);
531 kfree(rchunk);
532 if (status < 0)
533 break;
534 }
535 ocfs2_unlock_global_qf(oinfo, 1);
536out:
537 if (status < 0)
538 free_recovery_list(&(rec->r_list[type]));
539 mlog_exit(status);
540 return status;
541}
542
543/* Recover local quota files for given node different from us */
544int ocfs2_finish_quota_recovery(struct ocfs2_super *osb,
545 struct ocfs2_quota_recovery *rec,
546 int slot_num)
547{
548 unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE,
549 LOCAL_GROUP_QUOTA_SYSTEM_INODE };
550 struct super_block *sb = osb->sb;
551 struct ocfs2_local_disk_dqinfo *ldinfo;
552 struct buffer_head *bh;
553 handle_t *handle;
554 int type;
555 int status = 0;
556 struct inode *lqinode;
557 unsigned int flags;
558
559 mlog(ML_NOTICE, "Finishing quota recovery in slot %u\n", slot_num);
560 mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
561 for (type = 0; type < MAXQUOTAS; type++) {
562 if (list_empty(&(rec->r_list[type])))
563 continue;
564 mlog(0, "Recovering quota in slot %d\n", slot_num);
565 lqinode = ocfs2_get_system_file_inode(osb, ino[type], slot_num);
566 if (!lqinode) {
567 status = -ENOENT;
568 goto out;
569 }
570 status = ocfs2_inode_lock_full(lqinode, NULL, 1,
571 OCFS2_META_LOCK_NOQUEUE);
572 /* Someone else is holding the lock? Then he must be
573 * doing the recovery. Just skip the file... */
574 if (status == -EAGAIN) {
575 mlog(ML_NOTICE, "skipping quota recovery for slot %d "
576 "because quota file is locked.\n", slot_num);
577 status = 0;
578 goto out_put;
579 } else if (status < 0) {
580 mlog_errno(status);
581 goto out_put;
582 }
583 /* Now read local header */
584 bh = ocfs2_read_quota_block(lqinode, 0, &status);
585 if (!bh) {
586 mlog_errno(status);
587 mlog(ML_ERROR, "failed to read quota file info header "
588 "(slot=%d type=%d)\n", slot_num, type);
589 goto out_lock;
590 }
591 ldinfo = (struct ocfs2_local_disk_dqinfo *)(bh->b_data +
592 OCFS2_LOCAL_INFO_OFF);
593 /* Is recovery still needed? */
594 flags = le32_to_cpu(ldinfo->dqi_flags);
595 if (!(flags & OLQF_CLEAN))
596 status = ocfs2_recover_local_quota_file(lqinode,
597 type,
598 rec);
599 /* We don't want to mark file as clean when it is actually
600 * active */
601 if (slot_num == osb->slot_num)
602 goto out_bh;
603 /* Mark quota file as clean if we are recovering quota file of
604 * some other node. */
605 handle = ocfs2_start_trans(osb, 1);
606 if (IS_ERR(handle)) {
607 status = PTR_ERR(handle);
608 mlog_errno(status);
609 goto out_bh;
610 }
611 status = ocfs2_journal_access(handle, lqinode, bh,
612 OCFS2_JOURNAL_ACCESS_WRITE);
613 if (status < 0) {
614 mlog_errno(status);
615 goto out_trans;
616 }
617 lock_buffer(bh);
618 ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN);
619 unlock_buffer(bh);
620 status = ocfs2_journal_dirty(handle, bh);
621 if (status < 0)
622 mlog_errno(status);
623out_trans:
624 ocfs2_commit_trans(osb, handle);
625out_bh:
626 brelse(bh);
627out_lock:
628 ocfs2_inode_unlock(lqinode, 1);
629out_put:
630 iput(lqinode);
631 if (status < 0)
632 break;
633 }
634out:
635 mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
636 kfree(rec);
637 return status;
638}
639
256/* Read information header from quota file */ 640/* Read information header from quota file */
257static int ocfs2_local_read_info(struct super_block *sb, int type) 641static int ocfs2_local_read_info(struct super_block *sb, int type)
258{ 642{
@@ -262,6 +646,7 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
262 struct inode *lqinode = sb_dqopt(sb)->files[type]; 646 struct inode *lqinode = sb_dqopt(sb)->files[type];
263 int status; 647 int status;
264 struct buffer_head *bh = NULL; 648 struct buffer_head *bh = NULL;
649 struct ocfs2_quota_recovery *rec;
265 int locked = 0; 650 int locked = 0;
266 651
267 info->dqi_maxblimit = 0x7fffffffffffffffLL; 652 info->dqi_maxblimit = 0x7fffffffffffffffLL;
@@ -275,6 +660,7 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
275 info->dqi_priv = oinfo; 660 info->dqi_priv = oinfo;
276 oinfo->dqi_type = type; 661 oinfo->dqi_type = type;
277 INIT_LIST_HEAD(&oinfo->dqi_chunk); 662 INIT_LIST_HEAD(&oinfo->dqi_chunk);
663 oinfo->dqi_rec = NULL;
278 oinfo->dqi_lqi_bh = NULL; 664 oinfo->dqi_lqi_bh = NULL;
279 oinfo->dqi_ibh = NULL; 665 oinfo->dqi_ibh = NULL;
280 666
@@ -305,10 +691,27 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
305 oinfo->dqi_ibh = bh; 691 oinfo->dqi_ibh = bh;
306 692
307 /* We crashed when using local quota file? */ 693 /* We crashed when using local quota file? */
308 if (!(info->dqi_flags & OLQF_CLEAN)) 694 if (!(info->dqi_flags & OLQF_CLEAN)) {
309 goto out_err; /* So far we just bail out. Later we should resync here */ 695 rec = OCFS2_SB(sb)->quota_rec;
696 if (!rec) {
697 rec = ocfs2_alloc_quota_recovery();
698 if (!rec) {
699 status = -ENOMEM;
700 mlog_errno(status);
701 goto out_err;
702 }
703 OCFS2_SB(sb)->quota_rec = rec;
704 }
310 705
311 status = ocfs2_load_local_quota_bitmaps(sb_dqopt(sb)->files[type], 706 status = ocfs2_recovery_load_quota(lqinode, ldinfo, type,
707 &rec->r_list[type]);
708 if (status < 0) {
709 mlog_errno(status);
710 goto out_err;
711 }
712 }
713
714 status = ocfs2_load_local_quota_bitmaps(lqinode,
312 ldinfo, 715 ldinfo,
313 &oinfo->dqi_chunk); 716 &oinfo->dqi_chunk);
314 if (status < 0) { 717 if (status < 0) {
@@ -394,6 +797,12 @@ static int ocfs2_local_free_info(struct super_block *sb, int type)
394 } 797 }
395 ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk); 798 ocfs2_release_local_quota_bitmaps(&oinfo->dqi_chunk);
396 799
800 /* dqonoff_mutex protects us against racing with recovery thread... */
801 if (oinfo->dqi_rec) {
802 ocfs2_free_quota_recovery(oinfo->dqi_rec);
803 mark_clean = 0;
804 }
805
397 if (!mark_clean) 806 if (!mark_clean)
398 goto out; 807 goto out;
399 808