Skip to content

Commit

Permalink
[GFS2] Journaled file write/unstuff bug
Browse files Browse the repository at this point in the history
This patch is for bugzilla bug 283162, which uncovered a number of
bugs pertaining to writing to files that have the journaled bit on.
These bugs happen most often when writing to the meta_fs because
the files are always journaled.  So operations like gfs2_grow were
particularly vulnerable, although many of the problems could be
recreated with normal files after setting the journaled bit on.
The problems fixed are:

-GFS2 wasn't ever writing unstuffed journaled data blocks to their
 in-place location on disk. Now it does.

-If you unmounted too quickly after doing IO to a journaled file,
 GFS2 was crashing because you would discard a buffer whose bufdata
 was still on the active items list.  GFS2 now deals with this
 gracefully.

-GFS2 was losing track of the bufdata for journaled data blocks,
 and it wasn't getting freed, causing an error when you tried to
 unmount the module.  GFS2 now frees all the bufdata structures.

-There was a memory corruption occurring because GFS2 wrote
 twice as many log entries for journaled buffers.

-It was occasionally trying to write journal headers in buffers
 that weren't currently mapped.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
  • Loading branch information
Robert Peterson authored and Steven Whitehouse committed Jul 9, 2007
1 parent fad59c1 commit 8fb6859
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 3 deletions.
15 changes: 14 additions & 1 deletion fs/gfs2/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)

gfs2_assert(sdp, bd->bd_ail == ai);

if (!bh){
list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
continue;
}

if (!buffer_busy(bh)) {
if (!buffer_uptodate(bh)) {
gfs2_log_unlock(sdp);
Expand Down Expand Up @@ -125,6 +130,11 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
bd_ail_st_list) {
bh = bd->bd_bh;

if (!bh){
list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
continue;
}

gfs2_assert(sdp, bd->bd_ail == ai);

if (buffer_busy(bh)) {
Expand Down Expand Up @@ -227,7 +237,10 @@ static void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
list_del(&bd->bd_ail_st_list);
list_del(&bd->bd_ail_gl_list);
atomic_dec(&bd->bd_gl->gl_ail_count);
brelse(bd->bd_bh);
if (bd->bd_bh)
brelse(bd->bd_bh);
else
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
}

Expand Down
4 changes: 3 additions & 1 deletion fs/gfs2/lops.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,8 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
if (unlikely(magic != 0))
set_buffer_escaped(bh1);
gfs2_log_lock(sdp);
if (n++ > num)
n += 2;
if (n >= num)
break;
} else if (!bh1) {
total_dbuf--;
Expand All @@ -624,6 +625,7 @@ static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
}
gfs2_log_unlock(sdp);
if (bh) {
set_buffer_mapped(bh);
set_buffer_dirty(bh);
ll_rw_block(WRITE, 1, &bh);
bh = NULL;
Expand Down
26 changes: 25 additions & 1 deletion fs/gfs2/ops_address.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ static int gfs2_writepage(struct page *page, struct writeback_control *wbc)
return 0; /* don't care */
}

if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) {
if ((sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) &&
PageChecked(page)) {
ClearPageChecked(page);
error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0);
if (error)
goto out_ignore;
Expand Down Expand Up @@ -573,6 +575,23 @@ static int gfs2_commit_write(struct file *file, struct page *page,
return error;
}

/**
* gfs2_set_page_dirty - Page dirtying function
* @page: The page to dirty
*
* Returns: 1 if it dirtyed the page, or 0 otherwise
*/

static int gfs2_set_page_dirty(struct page *page)
{
struct gfs2_inode *ip = GFS2_I(page->mapping->host);
struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);

if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip))
SetPageChecked(page);
return __set_page_dirty_buffers(page);
}

/**
* gfs2_bmap - Block map function
* @mapping: Address space info
Expand Down Expand Up @@ -609,6 +628,8 @@ static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh)
if (bd) {
bd->bd_bh = NULL;
bh->b_private = NULL;
if (!bd->bd_ail && list_empty(&bd->bd_le.le_list))
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
gfs2_log_unlock(sdp);

Expand All @@ -629,6 +650,8 @@ static void gfs2_invalidatepage(struct page *page, unsigned long offset)
unsigned int curr_off = 0;

BUG_ON(!PageLocked(page));
if (offset == 0)
ClearPageChecked(page);
if (!page_has_buffers(page))
return;

Expand Down Expand Up @@ -841,6 +864,7 @@ const struct address_space_operations gfs2_file_aops = {
.sync_page = block_sync_page,
.prepare_write = gfs2_prepare_write,
.commit_write = gfs2_commit_write,
.set_page_dirty = gfs2_set_page_dirty,
.bmap = gfs2_bmap,
.invalidatepage = gfs2_invalidatepage,
.releasepage = gfs2_releasepage,
Expand Down

0 comments on commit 8fb6859

Please sign in to comment.