Skip to content

Commit

Permalink
[GFS2] Fix releasepage bug (fixes direct i/o writes)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Steven Whitehouse committed Aug 31, 2006
1 parent 899be4d commit 623d935
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 4 deletions.
11 changes: 11 additions & 0 deletions fs/gfs2/lops.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,17 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
gfs2_log_lock(sdp);
if (n++ > num)
break;
} else if (!bd1->bd_bh) {
total_dbuf--;
sdp->sd_log_num_databuf--;
list_del_init(&bd1->bd_le.le_list);
if (bd1 == bd2) {
bd2 = NULL;
bd2 = list_prepare_entry(bd2,
&sdp->sd_log_le_databuf,
bd_le.le_list);
}
kmem_cache_free(gfs2_bufdata_cachep, bd1);
}
}
gfs2_log_unlock(sdp);
Expand Down
33 changes: 29 additions & 4 deletions fs/gfs2/ops_address.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,25 @@ static int get_block_noalloc(struct inode *inode, sector_t lblock,
return error;
}

static int get_block_direct(struct inode *inode, sector_t lblock,
struct buffer_head *bh_result, int create)
{
int new = 0;
u64 dblock;
int error, boundary;

error = gfs2_block_map(inode, lblock, &new, &dblock, &boundary);
if (error)
return error;

if (dblock) {
map_bh(bh_result, inode->i_sb, dblock);
if (boundary)
set_buffer_boundary(bh_result);
}

return 0;
}
/**
* gfs2_writepage - Write complete page
* @page: Page to write
Expand Down Expand Up @@ -661,7 +680,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
rv = blockdev_direct_IO_own_locking(rw, iocb, inode,
inode->i_sb->s_bdev,
iov, offset, nr_segs,
gfs2_get_block, NULL);
get_block_direct, NULL);
out:
gfs2_glock_dq_m(1, &gh);
gfs2_holder_uninit(&gh);
Expand Down Expand Up @@ -724,7 +743,7 @@ static unsigned limit = 0;
}

/**
* gfs2_aspace_releasepage - free the metadata associated with a page
* gfs2_releasepage - free the metadata associated with a page
* @page: the page that's being released
* @gfp_mask: passed from Linux VFS, ignored by us
*
Expand Down Expand Up @@ -761,16 +780,22 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
}

gfs2_assert_warn(sdp, !buffer_pinned(bh));
gfs2_assert_warn(sdp, !buffer_dirty(bh));

gfs2_log_lock(sdp);
bd = bh->b_private;
if (bd) {
gfs2_assert_warn(sdp, bd->bd_bh == bh);
gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr));
gfs2_assert_warn(sdp, list_empty(&bd->bd_le.le_list));
gfs2_assert_warn(sdp, !bd->bd_ail);
kmem_cache_free(gfs2_bufdata_cachep, bd);
bd->bd_bh = NULL;
if (!list_empty(&bd->bd_le.le_list))
bd = NULL;
bh->b_private = NULL;
}
gfs2_log_unlock(sdp);
if (bd)
kmem_cache_free(gfs2_bufdata_cachep, bd);

bh = bh->b_this_page;
} while (bh != head);
Expand Down

0 comments on commit 623d935

Please sign in to comment.