Skip to content

Commit

Permalink
GFS2: Clean up ->page_mkwrite
Browse files Browse the repository at this point in the history
This patch brings gfs2's ->page_mkwrite uptodate with respect to the
expectations set by the VM. Also added is a check to wait if the fs
is frozen, before we attempt to get a glock. This will only work on
the node which initiates the freeze, but thats ok since the transaction
lock will still provide the expected barrier on other nodes.

The major change here is that we return a locked page now, except when
we don't return a page at all (error cases). This removes the race
which required rechecking the page after it was returned.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Cc: Nick Piggin <npiggin@kernel.dk>
  • Loading branch information
Steven Whitehouse committed Oct 21, 2011
1 parent ccad4e1 commit 13d921e
Showing 1 changed file with 46 additions and 18 deletions.
64 changes: 46 additions & 18 deletions fs/gfs2/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,15 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
unsigned int data_blocks, ind_blocks, rblocks;
struct gfs2_holder gh;
struct gfs2_alloc *al;
loff_t size;
int ret;

/* Wait if fs is frozen. This is racy so we check again later on
* and retry if the fs has been frozen after the page lock has
* been acquired
*/
vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);

gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret)
Expand All @@ -376,8 +383,15 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
set_bit(GLF_DIRTY, &ip->i_gl->gl_flags);
set_bit(GIF_SW_PAGED, &ip->i_flags);

if (!gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE))
if (!gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE)) {
lock_page(page);
if (!PageUptodate(page) || page->mapping != inode->i_mapping) {
ret = -EAGAIN;
unlock_page(page);
}
goto out_unlock;
}

ret = -ENOMEM;
al = gfs2_alloc_get(ip);
if (al == NULL)
Expand Down Expand Up @@ -405,21 +419,29 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)

lock_page(page);
ret = -EINVAL;
last_index = ip->i_inode.i_size >> PAGE_CACHE_SHIFT;
if (page->index > last_index)
goto out_unlock_page;
size = i_size_read(inode);
last_index = (size - 1) >> PAGE_CACHE_SHIFT;
/* Check page index against inode size */
if (size == 0 || (page->index > last_index))
goto out_trans_end;

ret = -EAGAIN;
/* If truncated, we must retry the operation, we may have raced
* with the glock demotion code.
*/
if (!PageUptodate(page) || page->mapping != inode->i_mapping)
goto out_trans_end;

/* Unstuff, if required, and allocate backing blocks for page */
ret = 0;
if (!PageUptodate(page) || page->mapping != ip->i_inode.i_mapping)
goto out_unlock_page;
if (gfs2_is_stuffed(ip)) {
if (gfs2_is_stuffed(ip))
ret = gfs2_unstuff_dinode(ip, page);
if (ret)
goto out_unlock_page;
}
ret = gfs2_allocate_page_backing(page);
if (ret == 0)
ret = gfs2_allocate_page_backing(page);

out_unlock_page:
unlock_page(page);
out_trans_end:
if (ret)
unlock_page(page);
gfs2_trans_end(sdp);
out_trans_fail:
gfs2_inplace_release(ip);
Expand All @@ -431,11 +453,17 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
gfs2_glock_dq(&gh);
out:
gfs2_holder_uninit(&gh);
if (ret == -ENOMEM)
ret = VM_FAULT_OOM;
else if (ret)
ret = VM_FAULT_SIGBUS;
return ret;
if (ret == 0) {
set_page_dirty(page);
/* This check must be post dropping of transaction lock */
if (inode->i_sb->s_frozen == SB_UNFROZEN) {
wait_on_page_writeback(page);
} else {
ret = -EAGAIN;
unlock_page(page);
}
}
return block_page_mkwrite_return(ret);
}

static const struct vm_operations_struct gfs2_vm_ops = {
Expand Down

0 comments on commit 13d921e

Please sign in to comment.