diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2007-09-17 05:59:52 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2007-10-10 03:56:24 -0400 |
commit | 16615be18cadf53ee6f8a4f0bdd647f0753421b1 (patch) | |
tree | 670c75e931e6d606211f338ee5e8b1d603c96521 /fs/gfs2/log.c | |
parent | 55c0c4ac0be144014651b19e77c9b77f367955de (diff) |
[GFS2] Clean up journaled data writing
This patch cleans up the code for writing journaled data into the log.
It also removes the need to allocate a small "tag" structure for each
block written into the log. Instead we just keep count of the outstanding
I/O so that we can be sure that its all been written at the correct time.
Another result of this patch is that a number of ll_rw_block() calls
have become submit_bh() calls, closing some races at the same time.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2/log.c')
-rw-r--r-- | fs/gfs2/log.c | 145 |
1 files changed, 83 insertions, 62 deletions
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 4d04e6f19706..ee704676b2f1 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c | |||
@@ -104,11 +104,8 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) | |||
104 | gfs2_assert(sdp, bd->bd_ail == ai); | 104 | gfs2_assert(sdp, bd->bd_ail == ai); |
105 | 105 | ||
106 | if (!buffer_busy(bh)) { | 106 | if (!buffer_busy(bh)) { |
107 | if (!buffer_uptodate(bh)) { | 107 | if (!buffer_uptodate(bh)) |
108 | gfs2_log_unlock(sdp); | ||
109 | gfs2_io_error_bh(sdp, bh); | 108 | gfs2_io_error_bh(sdp, bh); |
110 | gfs2_log_lock(sdp); | ||
111 | } | ||
112 | list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); | 109 | list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); |
113 | continue; | 110 | continue; |
114 | } | 111 | } |
@@ -118,9 +115,16 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) | |||
118 | 115 | ||
119 | list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); | 116 | list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); |
120 | 117 | ||
118 | get_bh(bh); | ||
121 | gfs2_log_unlock(sdp); | 119 | gfs2_log_unlock(sdp); |
122 | wait_on_buffer(bh); | 120 | lock_buffer(bh); |
123 | ll_rw_block(WRITE, 1, &bh); | 121 | if (test_clear_buffer_dirty(bh)) { |
122 | bh->b_end_io = end_buffer_write_sync; | ||
123 | submit_bh(WRITE, bh); | ||
124 | } else { | ||
125 | unlock_buffer(bh); | ||
126 | brelse(bh); | ||
127 | } | ||
124 | gfs2_log_lock(sdp); | 128 | gfs2_log_lock(sdp); |
125 | 129 | ||
126 | retry = 1; | 130 | retry = 1; |
@@ -446,10 +450,10 @@ static unsigned int current_tail(struct gfs2_sbd *sdp) | |||
446 | return tail; | 450 | return tail; |
447 | } | 451 | } |
448 | 452 | ||
449 | static inline void log_incr_head(struct gfs2_sbd *sdp) | 453 | void gfs2_log_incr_head(struct gfs2_sbd *sdp) |
450 | { | 454 | { |
451 | if (sdp->sd_log_flush_head == sdp->sd_log_tail) | 455 | if (sdp->sd_log_flush_head == sdp->sd_log_tail) |
452 | gfs2_assert_withdraw(sdp, sdp->sd_log_flush_head == sdp->sd_log_head); | 456 | BUG_ON(sdp->sd_log_flush_head != sdp->sd_log_head); |
453 | 457 | ||
454 | if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) { | 458 | if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) { |
455 | sdp->sd_log_flush_head = 0; | 459 | sdp->sd_log_flush_head = 0; |
@@ -458,6 +462,23 @@ static inline void log_incr_head(struct gfs2_sbd *sdp) | |||
458 | } | 462 | } |
459 | 463 | ||
460 | /** | 464 | /** |
465 | * gfs2_log_write_endio - End of I/O for a log buffer | ||
466 | * @bh: The buffer head | ||
467 | * @uptodate: I/O Status | ||
468 | * | ||
469 | */ | ||
470 | |||
471 | static void gfs2_log_write_endio(struct buffer_head *bh, int uptodate) | ||
472 | { | ||
473 | struct gfs2_sbd *sdp = bh->b_private; | ||
474 | bh->b_private = NULL; | ||
475 | |||
476 | end_buffer_write_sync(bh, uptodate); | ||
477 | if (atomic_dec_and_test(&sdp->sd_log_in_flight)) | ||
478 | wake_up(&sdp->sd_log_flush_wait); | ||
479 | } | ||
480 | |||
481 | /** | ||
461 | * gfs2_log_get_buf - Get and initialize a buffer to use for log control data | 482 | * gfs2_log_get_buf - Get and initialize a buffer to use for log control data |
462 | * @sdp: The GFS2 superblock | 483 | * @sdp: The GFS2 superblock |
463 | * | 484 | * |
@@ -467,25 +488,42 @@ static inline void log_incr_head(struct gfs2_sbd *sdp) | |||
467 | struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp) | 488 | struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp) |
468 | { | 489 | { |
469 | u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); | 490 | u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); |
470 | struct gfs2_log_buf *lb; | ||
471 | struct buffer_head *bh; | 491 | struct buffer_head *bh; |
472 | 492 | ||
473 | lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL); | 493 | bh = sb_getblk(sdp->sd_vfs, blkno); |
474 | list_add(&lb->lb_list, &sdp->sd_log_flush_list); | ||
475 | |||
476 | bh = lb->lb_bh = sb_getblk(sdp->sd_vfs, blkno); | ||
477 | lock_buffer(bh); | 494 | lock_buffer(bh); |
478 | memset(bh->b_data, 0, bh->b_size); | 495 | memset(bh->b_data, 0, bh->b_size); |
479 | set_buffer_uptodate(bh); | 496 | set_buffer_uptodate(bh); |
480 | clear_buffer_dirty(bh); | 497 | clear_buffer_dirty(bh); |
481 | unlock_buffer(bh); | 498 | gfs2_log_incr_head(sdp); |
482 | 499 | atomic_inc(&sdp->sd_log_in_flight); | |
483 | log_incr_head(sdp); | 500 | bh->b_private = sdp; |
501 | bh->b_end_io = gfs2_log_write_endio; | ||
484 | 502 | ||
485 | return bh; | 503 | return bh; |
486 | } | 504 | } |
487 | 505 | ||
488 | /** | 506 | /** |
507 | * gfs2_fake_write_endio - | ||
508 | * @bh: The buffer head | ||
509 | * @uptodate: The I/O Status | ||
510 | * | ||
511 | */ | ||
512 | |||
513 | static void gfs2_fake_write_endio(struct buffer_head *bh, int uptodate) | ||
514 | { | ||
515 | struct buffer_head *real_bh = bh->b_private; | ||
516 | struct gfs2_sbd *sdp = GFS2_SB(real_bh->b_page->mapping->host); | ||
517 | |||
518 | end_buffer_write_sync(bh, uptodate); | ||
519 | free_buffer_head(bh); | ||
520 | unlock_buffer(real_bh); | ||
521 | brelse(real_bh); | ||
522 | if (atomic_dec_and_test(&sdp->sd_log_in_flight)) | ||
523 | wake_up(&sdp->sd_log_flush_wait); | ||
524 | } | ||
525 | |||
526 | /** | ||
489 | * gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log | 527 | * gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log |
490 | * @sdp: the filesystem | 528 | * @sdp: the filesystem |
491 | * @data: the data the buffer_head should point to | 529 | * @data: the data the buffer_head should point to |
@@ -497,22 +535,20 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, | |||
497 | struct buffer_head *real) | 535 | struct buffer_head *real) |
498 | { | 536 | { |
499 | u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); | 537 | u64 blkno = log_bmap(sdp, sdp->sd_log_flush_head); |
500 | struct gfs2_log_buf *lb; | ||
501 | struct buffer_head *bh; | 538 | struct buffer_head *bh; |
502 | 539 | ||
503 | lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_NOFS | __GFP_NOFAIL); | 540 | bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL); |
504 | list_add(&lb->lb_list, &sdp->sd_log_flush_list); | ||
505 | lb->lb_real = real; | ||
506 | |||
507 | bh = lb->lb_bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL); | ||
508 | atomic_set(&bh->b_count, 1); | 541 | atomic_set(&bh->b_count, 1); |
509 | bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate); | 542 | bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate) | (1 << BH_Lock); |
510 | set_bh_page(bh, real->b_page, bh_offset(real)); | 543 | set_bh_page(bh, real->b_page, bh_offset(real)); |
511 | bh->b_blocknr = blkno; | 544 | bh->b_blocknr = blkno; |
512 | bh->b_size = sdp->sd_sb.sb_bsize; | 545 | bh->b_size = sdp->sd_sb.sb_bsize; |
513 | bh->b_bdev = sdp->sd_vfs->s_bdev; | 546 | bh->b_bdev = sdp->sd_vfs->s_bdev; |
547 | bh->b_private = real; | ||
548 | bh->b_end_io = gfs2_fake_write_endio; | ||
514 | 549 | ||
515 | log_incr_head(sdp); | 550 | gfs2_log_incr_head(sdp); |
551 | atomic_inc(&sdp->sd_log_in_flight); | ||
516 | 552 | ||
517 | return bh; | 553 | return bh; |
518 | } | 554 | } |
@@ -579,45 +615,24 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) | |||
579 | gfs2_assert_withdraw(sdp, !pull); | 615 | gfs2_assert_withdraw(sdp, !pull); |
580 | 616 | ||
581 | sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); | 617 | sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); |
582 | log_incr_head(sdp); | 618 | gfs2_log_incr_head(sdp); |
583 | } | 619 | } |
584 | 620 | ||
585 | static void log_flush_commit(struct gfs2_sbd *sdp) | 621 | static void log_flush_commit(struct gfs2_sbd *sdp) |
586 | { | 622 | { |
587 | struct list_head *head = &sdp->sd_log_flush_list; | 623 | DEFINE_WAIT(wait); |
588 | struct gfs2_log_buf *lb; | 624 | |
589 | struct buffer_head *bh; | 625 | if (atomic_read(&sdp->sd_log_in_flight)) { |
590 | int flushcount = 0; | 626 | do { |
591 | 627 | prepare_to_wait(&sdp->sd_log_flush_wait, &wait, | |
592 | while (!list_empty(head)) { | 628 | TASK_UNINTERRUPTIBLE); |
593 | lb = list_entry(head->next, struct gfs2_log_buf, lb_list); | 629 | if (atomic_read(&sdp->sd_log_in_flight)) |
594 | list_del(&lb->lb_list); | 630 | io_schedule(); |
595 | bh = lb->lb_bh; | 631 | } while(atomic_read(&sdp->sd_log_in_flight)); |
596 | 632 | finish_wait(&sdp->sd_log_flush_wait, &wait); | |
597 | wait_on_buffer(bh); | ||
598 | if (!buffer_uptodate(bh)) | ||
599 | gfs2_io_error_bh(sdp, bh); | ||
600 | if (lb->lb_real) { | ||
601 | while (atomic_read(&bh->b_count) != 1) /* Grrrr... */ | ||
602 | schedule(); | ||
603 | free_buffer_head(bh); | ||
604 | } else | ||
605 | brelse(bh); | ||
606 | kfree(lb); | ||
607 | flushcount++; | ||
608 | } | 633 | } |
609 | 634 | ||
610 | /* If nothing was journaled, the header is unplanned and unwanted. */ | 635 | log_write_header(sdp, 0, 0); |
611 | if (flushcount) { | ||
612 | log_write_header(sdp, 0, 0); | ||
613 | } else { | ||
614 | unsigned int tail; | ||
615 | tail = current_tail(sdp); | ||
616 | |||
617 | gfs2_ail1_empty(sdp, 0); | ||
618 | if (sdp->sd_log_tail != tail) | ||
619 | log_pull_tail(sdp, tail); | ||
620 | } | ||
621 | } | 636 | } |
622 | 637 | ||
623 | static void gfs2_ordered_write(struct gfs2_sbd *sdp) | 638 | static void gfs2_ordered_write(struct gfs2_sbd *sdp) |
@@ -698,10 +713,16 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) | |||
698 | INIT_LIST_HEAD(&ai->ai_ail1_list); | 713 | INIT_LIST_HEAD(&ai->ai_ail1_list); |
699 | INIT_LIST_HEAD(&ai->ai_ail2_list); | 714 | INIT_LIST_HEAD(&ai->ai_ail2_list); |
700 | 715 | ||
701 | gfs2_assert_withdraw(sdp, | 716 | if (sdp->sd_log_num_buf != sdp->sd_log_commited_buf) { |
702 | sdp->sd_log_num_buf + sdp->sd_log_num_databuf == | 717 | printk(KERN_INFO "GFS2: log buf %u %u\n", sdp->sd_log_num_buf, |
703 | sdp->sd_log_commited_buf + | 718 | sdp->sd_log_commited_buf); |
704 | sdp->sd_log_commited_databuf); | 719 | gfs2_assert_withdraw(sdp, 0); |
720 | } | ||
721 | if (sdp->sd_log_num_databuf != sdp->sd_log_commited_databuf) { | ||
722 | printk(KERN_INFO "GFS2: log databuf %u %u\n", | ||
723 | sdp->sd_log_num_databuf, sdp->sd_log_commited_databuf); | ||
724 | gfs2_assert_withdraw(sdp, 0); | ||
725 | } | ||
705 | gfs2_assert_withdraw(sdp, | 726 | gfs2_assert_withdraw(sdp, |
706 | sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke); | 727 | sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke); |
707 | 728 | ||
@@ -713,7 +734,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl) | |||
713 | lops_before_commit(sdp); | 734 | lops_before_commit(sdp); |
714 | gfs2_ordered_wait(sdp); | 735 | gfs2_ordered_wait(sdp); |
715 | 736 | ||
716 | if (!list_empty(&sdp->sd_log_flush_list)) | 737 | if (sdp->sd_log_head != sdp->sd_log_flush_head) |
717 | log_flush_commit(sdp); | 738 | log_flush_commit(sdp); |
718 | else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){ | 739 | else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){ |
719 | gfs2_log_lock(sdp); | 740 | gfs2_log_lock(sdp); |