Commit 5906f819 authored by Stephen Lord's avatar Stephen Lord Committed by Christoph Hellwig

[XFS] cleanup delayed allocate write path a little and fix some

small bugs in there.

SGI Modid: 2.5.x-xfs:slinx:138445a
parent 7685cc34
...@@ -53,12 +53,15 @@ map_blocks( ...@@ -53,12 +53,15 @@ map_blocks(
count = max_t(ssize_t, count, XFS_WRITE_IO_LOG); count = max_t(ssize_t, count, XFS_WRITE_IO_LOG);
retry: retry:
VOP_BMAP(vp, offset, count, flags, pbmapp, &nmaps, error); VOP_BMAP(vp, offset, count, flags, pbmapp, &nmaps, error);
if (flags & PBF_WRITE) { if (error == EAGAIN)
if (unlikely((flags & PBF_DIRECT) && nmaps && return -error;
if (unlikely((flags & (PBF_WRITE|PBF_DIRECT)) ==
(PBF_WRITE|PBF_DIRECT) && nmaps &&
(pbmapp->pbm_flags & PBMF_DELAY))) { (pbmapp->pbm_flags & PBMF_DELAY))) {
flags = PBF_FILE_ALLOCATE; flags = PBF_FILE_ALLOCATE;
goto retry; goto retry;
} }
if (flags & (PBF_WRITE|PBF_FILE_ALLOCATE)) {
VMODIFY(vp); VMODIFY(vp);
} }
return -error; return -error;
...@@ -309,6 +312,7 @@ convert_page( ...@@ -309,6 +312,7 @@ convert_page(
if (startio && (offset < end)) { if (startio && (offset < end)) {
bh_arr[index++] = bh; bh_arr[index++] = bh;
} else { } else {
set_buffer_dirty(bh);
unlock_buffer(bh); unlock_buffer(bh);
} }
} while (i++, (bh = bh->b_this_page) != head); } while (i++, (bh = bh->b_this_page) != head);
...@@ -367,7 +371,7 @@ STATIC int ...@@ -367,7 +371,7 @@ STATIC int
delalloc_convert( delalloc_convert(
struct page *page, struct page *page,
int startio, int startio,
int allocate_space) int unmapped) /* also implies page uptodate */
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct buffer_head *bh_arr[MAX_BUF_PER_PAGE], *bh, *head; struct buffer_head *bh_arr[MAX_BUF_PER_PAGE], *bh, *head;
...@@ -375,6 +379,9 @@ delalloc_convert( ...@@ -375,6 +379,9 @@ delalloc_convert(
unsigned long p_offset = 0, end_index; unsigned long p_offset = 0, end_index;
loff_t offset, end_offset; loff_t offset, end_offset;
int len, err, i, cnt = 0, uptodate = 1; int len, err, i, cnt = 0, uptodate = 1;
int flags = startio ? 0 : PBF_TRYLOCK;
int page_dirty = 1;
/* Are we off the end of the file ? */ /* Are we off the end of the file ? */
end_index = inode->i_size >> PAGE_CACHE_SHIFT; end_index = inode->i_size >> PAGE_CACHE_SHIFT;
...@@ -390,9 +397,6 @@ delalloc_convert( ...@@ -390,9 +397,6 @@ delalloc_convert(
if (end_offset > inode->i_size) if (end_offset > inode->i_size)
end_offset = inode->i_size; end_offset = inode->i_size;
if (startio && !page_has_buffers(page))
create_empty_buffers(page, 1 << inode->i_blkbits, 0);
bh = head = page_buffers(page); bh = head = page_buffers(page);
mp = NULL; mp = NULL;
...@@ -406,10 +410,14 @@ delalloc_convert( ...@@ -406,10 +410,14 @@ delalloc_convert(
mp = match_offset_to_mapping(page, &map, p_offset); mp = match_offset_to_mapping(page, &map, p_offset);
} }
/*
* First case, allocate space for delalloc buffer head
* we can return EAGAIN here in the release page case.
*/
if (buffer_delay(bh)) { if (buffer_delay(bh)) {
if (!mp) { if (!mp) {
err = map_blocks(inode, offset, len, &map, err = map_blocks(inode, offset, len, &map,
PBF_FILE_ALLOCATE); PBF_FILE_ALLOCATE | flags);
if (err) { if (err) {
goto error; goto error;
} }
...@@ -422,11 +430,14 @@ delalloc_convert( ...@@ -422,11 +430,14 @@ delalloc_convert(
if (startio) { if (startio) {
bh_arr[cnt++] = bh; bh_arr[cnt++] = bh;
} else { } else {
set_buffer_dirty(bh);
unlock_buffer(bh); unlock_buffer(bh);
} }
page_dirty = 0;
} }
} else if ((buffer_uptodate(bh) || PageUptodate(page)) && } else if ((buffer_uptodate(bh) || PageUptodate(page)) &&
(allocate_space || startio)) { (unmapped || startio)) {
if (!buffer_mapped(bh)) { if (!buffer_mapped(bh)) {
int size; int size;
...@@ -454,13 +465,16 @@ delalloc_convert( ...@@ -454,13 +465,16 @@ delalloc_convert(
if (startio) { if (startio) {
bh_arr[cnt++] = bh; bh_arr[cnt++] = bh;
} else { } else {
set_buffer_dirty(bh);
unlock_buffer(bh); unlock_buffer(bh);
} }
page_dirty = 0;
} }
} else if (startio && buffer_mapped(bh)) { } else if (startio) {
if (buffer_uptodate(bh) && allocate_space) { if (buffer_uptodate(bh)) {
lock_buffer(bh); lock_buffer(bh);
bh_arr[cnt++] = bh; bh_arr[cnt++] = bh;
page_dirty = 0;
} }
} }
} }
...@@ -482,10 +496,10 @@ delalloc_convert( ...@@ -482,10 +496,10 @@ delalloc_convert(
if (mp) { if (mp) {
cluster_write(inode, page->index + 1, mp, cluster_write(inode, page->index + 1, mp,
startio, allocate_space); startio, unmapped);
} }
return 0; return page_dirty;
error: error:
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
...@@ -494,12 +508,15 @@ delalloc_convert( ...@@ -494,12 +508,15 @@ delalloc_convert(
/* /*
* If it's delalloc and we have nowhere to put it, * If it's delalloc and we have nowhere to put it,
* throw it away. * throw it away, unless the lower layers told
* us to try again.
*/ */
if (!allocate_space) { if (err != -EAGAIN) {
if (!unmapped) {
block_invalidatepage(page, 0); block_invalidatepage(page, 0);
} }
ClearPageUptodate(page); ClearPageUptodate(page);
}
return err; return err;
} }
...@@ -679,109 +696,172 @@ linvfs_readpages( ...@@ -679,109 +696,172 @@ linvfs_readpages(
} }
STATIC int STATIC void
count_page_state( count_page_state(
struct page *page, struct page *page,
int *nr_delalloc, int *delalloc,
int *nr_unmapped) int *unmapped)
{ {
*nr_delalloc = *nr_unmapped = 0;
if (page_has_buffers(page)) {
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
*delalloc = *unmapped = 0;
bh = head = page_buffers(page); bh = head = page_buffers(page);
do { do {
if (buffer_uptodate(bh) && !buffer_mapped(bh)) if (buffer_uptodate(bh) && !buffer_mapped(bh))
(*nr_unmapped)++; (*unmapped) = 1;
else if (buffer_delay(bh)) else if (buffer_delay(bh))
(*nr_delalloc)++; (*delalloc) = 1;
} while ((bh = bh->b_this_page) != head); } while ((bh = bh->b_this_page) != head);
return 1;
}
return 0;
} }
/*
* writepage: Called from one of two places:
*
* 1. we are flushing a delalloc buffer head.
*
* 2. we are writing out a dirty page. Typically the page dirty
* state is cleared before we get here. In this case is it
* conceivable we have no buffer heads.
*
* For delalloc space on the page we need to allocate space and
* flush it. For unmapped buffer heads on the page we should
* allocate space if the page is uptodate. For any other dirty
* buffer heads on the page we should flush them.
*
* If we detect that a transaction would be required to flush
* the page, we have to check the process flags first, if we
* are already in a transaction or disk I/O during allocations
* is off, we need to fail the writepage and redirty the page.
* We also need to set PF_NOIO ourselves.
*/
STATIC int STATIC int
linvfs_writepage( linvfs_writepage(
struct page *page, struct page *page,
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
int error; int error;
int need_trans = 1; int need_trans;
int nr_delalloc, nr_unmapped; int delalloc, unmapped;
struct inode *inode = page->mapping->host;
if (count_page_state(page, &nr_delalloc, &nr_unmapped)) /*
need_trans = nr_delalloc + nr_unmapped; * We need a transaction if:
* 1. There are delalloc buffers on the page
* 2. The page is upto date and we have unmapped buffers
* 3. The page is upto date and we have no buffers
*/
if (!page_has_buffers(page)) {
unmapped = 1;
need_trans = 1;
} else {
count_page_state(page, &delalloc, &unmapped);
if (!PageUptodate(page))
unmapped = 0;
need_trans = delalloc + unmapped;
}
/*
* If we need a transaction and the process flags say
* we are already in a transaction, or no IO is allowed
* then mark the page dirty again and leave the page
* as is.
*/
if ((current->flags & (PF_FSTRANS)) && need_trans) if ((current->flags & (PF_FSTRANS)) && need_trans)
goto out_fail; goto out_fail;
/*
* Delay hooking up buffer heads until we have
* made our go/no-go decision.
*/
if (!page_has_buffers(page)) {
create_empty_buffers(page, 1 << inode->i_blkbits, 0);
}
/* /*
* Convert delalloc or unmapped space to real space and flush out * Convert delalloc or unmapped space to real space and flush out
* to disk. * to disk.
*/ */
error = delalloc_convert(page, 1, nr_delalloc == 0); error = delalloc_convert(page, 1, unmapped);
if (unlikely(error)) if (error == -EAGAIN)
unlock_page(page); goto out_fail;
return error; if (unlikely(error < 0))
goto out_unlock;
return 0;
out_fail: out_fail:
set_page_dirty(page); set_page_dirty(page);
unlock_page(page); unlock_page(page);
return 0; return 0;
} out_unlock:
unlock_page(page);
STATIC int return error;
linvfs_prepare_write(
struct file *file,
struct page *page,
unsigned int from,
unsigned int to)
{
if (file && (file->f_flags & O_SYNC)) {
return block_prepare_write(page, from, to,
linvfs_get_block_sync);
} else {
return block_prepare_write(page, from, to,
linvfs_get_block);
}
} }
/* /*
* This gets a page into cleanable state - page locked on entry * Called to move a page into cleanable state - and from there
* kept locked on exit. If the page is marked dirty we should * to be released. Possibly the page is already clean. We always
* not come this way. * have buffer heads in this call.
*
* Returns 0 if the page is ok to release, 1 otherwise.
*
* Possible scenarios are:
*
* 1. We are being called to release a page which has been written
* to via regular I/O. buffer heads will be dirty and possibly
* delalloc. If no delalloc buffer heads in this case then we
* can just return zero.
*
* 2. We are called to release a page which has been written via
* mmap, all we need to do is ensure there is no delalloc
* state in the buffer heads, if not we can let the caller
* free them and we should come back later via writepage.
*/ */
STATIC int STATIC int
linvfs_release_page( linvfs_release_page(
struct page *page, struct page *page,
int gfp_mask) int gfp_mask)
{ {
int nr_delalloc, nr_unmapped; int delalloc, unmapped;
if (count_page_state(page, &nr_delalloc, &nr_unmapped)) { count_page_state(page, &delalloc, &unmapped);
if (!nr_delalloc) if (!delalloc)
goto free_buffers; goto free_buffers;
}
if (gfp_mask & __GFP_FS) { if (!(gfp_mask & __GFP_FS))
return 0;
/* /*
* Convert delalloc space to real space, do not flush the * Convert delalloc space to real space, do not flush the
* data out to disk, that will be done by the caller. * data out to disk, that will be done by the caller.
* Never need to allocate space here - we will always
* come back to writepage in that case.
*/ */
if (delalloc_convert(page, 0, 0) == 0) if (delalloc_convert(page, 0, 0) == 0)
goto free_buffers; goto free_buffers;
}
return 0; return 0;
free_buffers: free_buffers:
return try_to_free_buffers(page); return try_to_free_buffers(page);
} }
STATIC int
linvfs_prepare_write(
struct file *file,
struct page *page,
unsigned int from,
unsigned int to)
{
if (file && (file->f_flags & O_SYNC)) {
return block_prepare_write(page, from, to,
linvfs_get_block_sync);
} else {
return block_prepare_write(page, from, to,
linvfs_get_block);
}
}
struct address_space_operations linvfs_aops = { struct address_space_operations linvfs_aops = {
.readpage = linvfs_readpage, .readpage = linvfs_readpage,
......
...@@ -120,7 +120,13 @@ xfs_iomap( ...@@ -120,7 +120,13 @@ xfs_iomap(
case PBF_FILE_ALLOCATE: case PBF_FILE_ALLOCATE:
lockmode = XFS_ILOCK_SHARED|XFS_EXTSIZE_RD; lockmode = XFS_ILOCK_SHARED|XFS_EXTSIZE_RD;
bmap_flags = XFS_BMAPI_ENTIRE; bmap_flags = XFS_BMAPI_ENTIRE;
/* Attempt non-blocking lock */
if (flags & PBF_TRYLOCK) {
if (!XFS_ILOCK_NOWAIT(mp, io, lockmode))
return XFS_ERROR(EAGAIN);
} else {
XFS_ILOCK(mp, io, lockmode); XFS_ILOCK(mp, io, lockmode);
}
break; break;
case PBF_FILE_UNWRITTEN: case PBF_FILE_UNWRITTEN:
lockmode = XFS_ILOCK_EXCL|XFS_EXTSIZE_WR; lockmode = XFS_ILOCK_EXCL|XFS_EXTSIZE_WR;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment