aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2006-08-31 12:14:44 -0400
committerSteven Whitehouse <swhiteho@redhat.com>2006-08-31 12:14:44 -0400
commit623d93555c8884768db65ffc11509c93e50dd4db (patch)
tree08be5484217e9c7851b129d2794b4afe8e44760c
parent899be4d3b7e00bf364d84c1c8cfe8bbbd1e3507b (diff)
[GFS2] Fix releasepage bug (fixes direct i/o writes)
This patch fixes three main bugs. Firstly the direct i/o get_block was returning the wrong return code in certain cases. Secondly, the GFS2's releasepage function was not dealing with cases when clean, ordered buffers were found still queued on a transaction (which can happen depending on the ordering of journal flushes). Thirdly, the journaling code itself needed altering to take account of the after effects of removing the clean ordered buffers from the transactions before a journal flush. The releasepage bug did also show up under "normal" buffered i/o as well, so its not just a fix for direct i/o. In fact its not normally used in the direct i/o path at all, except when flushing existing buffers after performing a direct i/o write, but that was the code path that led us to spot this. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
-rw-r--r--fs/gfs2/lops.c11
-rw-r--r--fs/gfs2/ops_address.c33
2 files changed, 40 insertions, 4 deletions
diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c
index 08de8b7fb316..0ec38b399097 100644
--- a/fs/gfs2/lops.c
+++ b/fs/gfs2/lops.c
@@ -592,6 +592,17 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
592 gfs2_log_lock(sdp); 592 gfs2_log_lock(sdp);
593 if (n++ > num) 593 if (n++ > num)
594 break; 594 break;
595 } else if (!bd1->bd_bh) {
596 total_dbuf--;
597 sdp->sd_log_num_databuf--;
598 list_del_init(&bd1->bd_le.le_list);
599 if (bd1 == bd2) {
600 bd2 = NULL;
601 bd2 = list_prepare_entry(bd2,
602 &sdp->sd_log_le_databuf,
603 bd_le.le_list);
604 }
605 kmem_cache_free(gfs2_bufdata_cachep, bd1);
595 } 606 }
596 } 607 }
597 gfs2_log_unlock(sdp); 608 gfs2_log_unlock(sdp);
diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c
index 48720421c796..0de7a9523633 100644
--- a/fs/gfs2/ops_address.c
+++ b/fs/gfs2/ops_address.c
@@ -118,6 +118,25 @@ static int get_block_noalloc(struct inode *inode, sector_t lblock,
118 return error; 118 return error;
119} 119}
120 120
121static int get_block_direct(struct inode *inode, sector_t lblock,
122 struct buffer_head *bh_result, int create)
123{
124 int new = 0;
125 u64 dblock;
126 int error, boundary;
127
128 error = gfs2_block_map(inode, lblock, &new, &dblock, &boundary);
129 if (error)
130 return error;
131
132 if (dblock) {
133 map_bh(bh_result, inode->i_sb, dblock);
134 if (boundary)
135 set_buffer_boundary(bh_result);
136 }
137
138 return 0;
139}
121/** 140/**
122 * gfs2_writepage - Write complete page 141 * gfs2_writepage - Write complete page
123 * @page: Page to write 142 * @page: Page to write
@@ -661,7 +680,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
661 rv = blockdev_direct_IO_own_locking(rw, iocb, inode, 680 rv = blockdev_direct_IO_own_locking(rw, iocb, inode,
662 inode->i_sb->s_bdev, 681 inode->i_sb->s_bdev,
663 iov, offset, nr_segs, 682 iov, offset, nr_segs,
664 gfs2_get_block, NULL); 683 get_block_direct, NULL);
665out: 684out:
666 gfs2_glock_dq_m(1, &gh); 685 gfs2_glock_dq_m(1, &gh);
667 gfs2_holder_uninit(&gh); 686 gfs2_holder_uninit(&gh);
@@ -724,7 +743,7 @@ static unsigned limit = 0;
724} 743}
725 744
726/** 745/**
727 * gfs2_aspace_releasepage - free the metadata associated with a page 746 * gfs2_releasepage - free the metadata associated with a page
728 * @page: the page that's being released 747 * @page: the page that's being released
729 * @gfp_mask: passed from Linux VFS, ignored by us 748 * @gfp_mask: passed from Linux VFS, ignored by us
730 * 749 *
@@ -761,16 +780,22 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
761 } 780 }
762 781
763 gfs2_assert_warn(sdp, !buffer_pinned(bh)); 782 gfs2_assert_warn(sdp, !buffer_pinned(bh));
783 gfs2_assert_warn(sdp, !buffer_dirty(bh));
764 784
785 gfs2_log_lock(sdp);
765 bd = bh->b_private; 786 bd = bh->b_private;
766 if (bd) { 787 if (bd) {
767 gfs2_assert_warn(sdp, bd->bd_bh == bh); 788 gfs2_assert_warn(sdp, bd->bd_bh == bh);
768 gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); 789 gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr));
769 gfs2_assert_warn(sdp, list_empty(&bd->bd_le.le_list));
770 gfs2_assert_warn(sdp, !bd->bd_ail); 790 gfs2_assert_warn(sdp, !bd->bd_ail);
771 kmem_cache_free(gfs2_bufdata_cachep, bd); 791 bd->bd_bh = NULL;
792 if (!list_empty(&bd->bd_le.le_list))
793 bd = NULL;
772 bh->b_private = NULL; 794 bh->b_private = NULL;
773 } 795 }
796 gfs2_log_unlock(sdp);
797 if (bd)
798 kmem_cache_free(gfs2_bufdata_cachep, bd);
774 799
775 bh = bh->b_this_page; 800 bh = bh->b_this_page;
776 } while (bh != head); 801 } while (bh != head);