diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2007-08-16 11:03:57 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2007-10-10 03:55:29 -0400 |
commit | bb3b0e3df5420fdf2c6bbb4417525c6d2ef55bbb (patch) | |
tree | 6eab0fa06b4dc9f9a8d62cedaa6879327966a20c /fs/gfs2/ops_address.c | |
parent | 2d9a4bbf6d28673f4057682cc02d16bf288b4a35 (diff) |
[GFS2] Clean up invalidatepage/releasepage
This patch fixes some bugs relating to journaled data files by cleaning
up the gfs2_invalidatepage() and gfs2_releasepage() functions. We now
never block during gfs2_releasepage(), instead we always either release
or refuse to release depending on the status of the buffers.
This fixes Red Hat bugzillas #248969 and #252392.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Cc: Bob Peterson <rpeterso@redhat.com>
Diffstat (limited to 'fs/gfs2/ops_address.c')
-rw-r--r-- | fs/gfs2/ops_address.c | 132 |
1 files changed, 15 insertions, 117 deletions
diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 42a5f58f6fca..8407d1db4eac 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c | |||
@@ -616,58 +616,13 @@ static sector_t gfs2_bmap(struct address_space *mapping, sector_t lblock) | |||
616 | return dblock; | 616 | return dblock; |
617 | } | 617 | } |
618 | 618 | ||
619 | static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh) | ||
620 | { | ||
621 | struct gfs2_bufdata *bd; | ||
622 | |||
623 | gfs2_log_lock(sdp); | ||
624 | bd = bh->b_private; | ||
625 | if (bd) { | ||
626 | bd->bd_bh = NULL; | ||
627 | bh->b_private = NULL; | ||
628 | if (!bd->bd_ail && list_empty(&bd->bd_le.le_list)) | ||
629 | kmem_cache_free(gfs2_bufdata_cachep, bd); | ||
630 | } | ||
631 | gfs2_log_unlock(sdp); | ||
632 | |||
633 | lock_buffer(bh); | ||
634 | clear_buffer_dirty(bh); | ||
635 | bh->b_bdev = NULL; | ||
636 | clear_buffer_mapped(bh); | ||
637 | clear_buffer_req(bh); | ||
638 | clear_buffer_new(bh); | ||
639 | clear_buffer_delay(bh); | ||
640 | unlock_buffer(bh); | ||
641 | } | ||
642 | |||
643 | static void gfs2_invalidatepage(struct page *page, unsigned long offset) | 619 | static void gfs2_invalidatepage(struct page *page, unsigned long offset) |
644 | { | 620 | { |
645 | struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host); | ||
646 | struct buffer_head *head, *bh, *next; | ||
647 | unsigned int curr_off = 0; | ||
648 | |||
649 | BUG_ON(!PageLocked(page)); | 621 | BUG_ON(!PageLocked(page)); |
650 | if (offset == 0) | 622 | if (offset == 0) |
651 | ClearPageChecked(page); | 623 | ClearPageChecked(page); |
652 | if (!page_has_buffers(page)) | ||
653 | return; | ||
654 | 624 | ||
655 | bh = head = page_buffers(page); | 625 | block_invalidatepage(page, offset); |
656 | do { | ||
657 | unsigned int next_off = curr_off + bh->b_size; | ||
658 | next = bh->b_this_page; | ||
659 | |||
660 | if (offset <= curr_off) | ||
661 | discard_buffer(sdp, bh); | ||
662 | |||
663 | curr_off = next_off; | ||
664 | bh = next; | ||
665 | } while (bh != head); | ||
666 | |||
667 | if (!offset) | ||
668 | try_to_release_page(page, 0); | ||
669 | |||
670 | return; | ||
671 | } | 626 | } |
672 | 627 | ||
673 | /** | 628 | /** |
@@ -736,59 +691,6 @@ out: | |||
736 | } | 691 | } |
737 | 692 | ||
738 | /** | 693 | /** |
739 | * stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out. | ||
740 | * @bh: the buffer we're stuck on | ||
741 | * | ||
742 | */ | ||
743 | |||
744 | static void stuck_releasepage(struct buffer_head *bh) | ||
745 | { | ||
746 | struct inode *inode = bh->b_page->mapping->host; | ||
747 | struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; | ||
748 | struct gfs2_bufdata *bd = bh->b_private; | ||
749 | struct gfs2_glock *gl; | ||
750 | static unsigned limit = 0; | ||
751 | |||
752 | if (limit > 3) | ||
753 | return; | ||
754 | limit++; | ||
755 | |||
756 | fs_warn(sdp, "stuck in gfs2_releasepage() %p\n", inode); | ||
757 | fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", | ||
758 | (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); | ||
759 | fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); | ||
760 | fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); | ||
761 | |||
762 | if (!bd) | ||
763 | return; | ||
764 | |||
765 | gl = bd->bd_gl; | ||
766 | |||
767 | fs_warn(sdp, "gl = (%u, %llu)\n", | ||
768 | gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); | ||
769 | |||
770 | fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", | ||
771 | (list_empty(&bd->bd_list_tr)) ? "no" : "yes", | ||
772 | (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); | ||
773 | |||
774 | if (gl->gl_ops == &gfs2_inode_glops) { | ||
775 | struct gfs2_inode *ip = gl->gl_object; | ||
776 | unsigned int x; | ||
777 | |||
778 | if (!ip) | ||
779 | return; | ||
780 | |||
781 | fs_warn(sdp, "ip = %llu %llu\n", | ||
782 | (unsigned long long)ip->i_no_formal_ino, | ||
783 | (unsigned long long)ip->i_no_addr); | ||
784 | |||
785 | for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) | ||
786 | fs_warn(sdp, "ip->i_cache[%u] = %s\n", | ||
787 | x, (ip->i_cache[x]) ? "!NULL" : "NULL"); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | /** | ||
792 | * gfs2_releasepage - free the metadata associated with a page | 694 | * gfs2_releasepage - free the metadata associated with a page |
793 | * @page: the page that's being released | 695 | * @page: the page that's being released |
794 | * @gfp_mask: passed from Linux VFS, ignored by us | 696 | * @gfp_mask: passed from Linux VFS, ignored by us |
@@ -805,38 +707,31 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask) | |||
805 | struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; | 707 | struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; |
806 | struct buffer_head *bh, *head; | 708 | struct buffer_head *bh, *head; |
807 | struct gfs2_bufdata *bd; | 709 | struct gfs2_bufdata *bd; |
808 | unsigned long t = jiffies + gfs2_tune_get(sdp, gt_stall_secs) * HZ; | ||
809 | 710 | ||
810 | if (!page_has_buffers(page)) | 711 | if (!page_has_buffers(page)) |
811 | goto out; | 712 | goto out; |
812 | 713 | ||
714 | gfs2_log_lock(sdp); | ||
813 | head = bh = page_buffers(page); | 715 | head = bh = page_buffers(page); |
814 | do { | 716 | do { |
815 | while (atomic_read(&bh->b_count)) { | 717 | if (atomic_read(&bh->b_count)) |
816 | if (!atomic_read(&aspace->i_writecount)) | 718 | goto cannot_release; |
817 | return 0; | 719 | bd = bh->b_private; |
818 | 720 | if (bd && bd->bd_ail) | |
819 | if (!(gfp_mask & __GFP_WAIT)) | 721 | goto cannot_release; |
820 | return 0; | ||
821 | |||
822 | if (time_after_eq(jiffies, t)) { | ||
823 | stuck_releasepage(bh); | ||
824 | /* should we withdraw here? */ | ||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | yield(); | ||
829 | } | ||
830 | |||
831 | gfs2_assert_warn(sdp, !buffer_pinned(bh)); | 722 | gfs2_assert_warn(sdp, !buffer_pinned(bh)); |
832 | gfs2_assert_warn(sdp, !buffer_dirty(bh)); | 723 | gfs2_assert_warn(sdp, !buffer_dirty(bh)); |
724 | bh = bh->b_this_page; | ||
725 | } while(bh != head); | ||
726 | gfs2_log_unlock(sdp); | ||
833 | 727 | ||
728 | head = bh = page_buffers(page); | ||
729 | do { | ||
834 | gfs2_log_lock(sdp); | 730 | gfs2_log_lock(sdp); |
835 | bd = bh->b_private; | 731 | bd = bh->b_private; |
836 | if (bd) { | 732 | if (bd) { |
837 | gfs2_assert_warn(sdp, bd->bd_bh == bh); | 733 | gfs2_assert_warn(sdp, bd->bd_bh == bh); |
838 | gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); | 734 | gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); |
839 | gfs2_assert_warn(sdp, !bd->bd_ail); | ||
840 | bd->bd_bh = NULL; | 735 | bd->bd_bh = NULL; |
841 | if (!list_empty(&bd->bd_le.le_list)) | 736 | if (!list_empty(&bd->bd_le.le_list)) |
842 | bd = NULL; | 737 | bd = NULL; |
@@ -851,6 +746,9 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask) | |||
851 | 746 | ||
852 | out: | 747 | out: |
853 | return try_to_free_buffers(page); | 748 | return try_to_free_buffers(page); |
749 | cannot_release: | ||
750 | gfs2_log_unlock(sdp); | ||
751 | return 0; | ||
854 | } | 752 | } |
855 | 753 | ||
856 | const struct address_space_operations gfs2_file_aops = { | 754 | const struct address_space_operations gfs2_file_aops = { |