xfs: simplify xfs_vm_writepage

The writepage implementation in XFS still tries to deal with dirty but
unmapped buffers which used to caused by writes through shared mmaps.  Since
the introduction of ->page_mkwrite these can't happen anymore, so remove the
code dealing with them.

Note that the all_bh variable which causes us to start I/O on all buffers on
the pages was controlled by the count of unmapped buffers, which also
included those not actually dirty.  It's now unconditionally initialized to
0 but set to 1 for the case of small file size extensions.  It probably can
be removed entirely, but that's left for another patch.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
This commit is contained in:
Christoph Hellwig 2010-06-24 09:46:01 +10:00 committed by Alex Elder
parent 89f3b36396
commit 20cb52ebd1
3 changed files with 49 additions and 102 deletions

View File

@ -85,18 +85,15 @@ void
xfs_count_page_state( xfs_count_page_state(
struct page *page, struct page *page,
int *delalloc, int *delalloc,
int *unmapped,
int *unwritten) int *unwritten)
{ {
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
*delalloc = *unmapped = *unwritten = 0; *delalloc = *unwritten = 0;
bh = head = page_buffers(page); bh = head = page_buffers(page);
do { do {
if (buffer_uptodate(bh) && !buffer_mapped(bh)) if (buffer_unwritten(bh))
(*unmapped) = 1;
else if (buffer_unwritten(bh))
(*unwritten) = 1; (*unwritten) = 1;
else if (buffer_delay(bh)) else if (buffer_delay(bh))
(*delalloc) = 1; (*delalloc) = 1;
@ -607,31 +604,30 @@ xfs_map_at_offset(
STATIC unsigned int STATIC unsigned int
xfs_probe_page( xfs_probe_page(
struct page *page, struct page *page,
unsigned int pg_offset, unsigned int pg_offset)
int mapped)
{ {
struct buffer_head *bh, *head;
int ret = 0; int ret = 0;
if (PageWriteback(page)) if (PageWriteback(page))
return 0; return 0;
if (!PageDirty(page))
return 0;
if (!page->mapping)
return 0;
if (!page_has_buffers(page))
return 0;
if (page->mapping && PageDirty(page)) { bh = head = page_buffers(page);
if (page_has_buffers(page)) { do {
struct buffer_head *bh, *head; if (!buffer_uptodate(bh))
break;
bh = head = page_buffers(page); if (!buffer_mapped(bh))
do { break;
if (!buffer_uptodate(bh)) ret += bh->b_size;
break; if (ret >= pg_offset)
if (mapped != buffer_mapped(bh)) break;
break; } while ((bh = bh->b_this_page) != head);
ret += bh->b_size;
if (ret >= pg_offset)
break;
} while ((bh = bh->b_this_page) != head);
} else
ret = mapped ? 0 : PAGE_CACHE_SIZE;
}
return ret; return ret;
} }
@ -641,8 +637,7 @@ xfs_probe_cluster(
struct inode *inode, struct inode *inode,
struct page *startpage, struct page *startpage,
struct buffer_head *bh, struct buffer_head *bh,
struct buffer_head *head, struct buffer_head *head)
int mapped)
{ {
struct pagevec pvec; struct pagevec pvec;
pgoff_t tindex, tlast, tloff; pgoff_t tindex, tlast, tloff;
@ -651,7 +646,7 @@ xfs_probe_cluster(
/* First sum forwards in this page */ /* First sum forwards in this page */
do { do {
if (!buffer_uptodate(bh) || (mapped != buffer_mapped(bh))) if (!buffer_uptodate(bh) || !buffer_mapped(bh))
return total; return total;
total += bh->b_size; total += bh->b_size;
} while ((bh = bh->b_this_page) != head); } while ((bh = bh->b_this_page) != head);
@ -685,7 +680,7 @@ xfs_probe_cluster(
pg_offset = PAGE_CACHE_SIZE; pg_offset = PAGE_CACHE_SIZE;
if (page->index == tindex && trylock_page(page)) { if (page->index == tindex && trylock_page(page)) {
pg_len = xfs_probe_page(page, pg_offset, mapped); pg_len = xfs_probe_page(page, pg_offset);
unlock_page(page); unlock_page(page);
} }
@ -1021,18 +1016,12 @@ out_invalidate:
* For delalloc space on the page we need to allocate space and flush it. * For delalloc space on the page we need to allocate space and flush it.
* For unwritten space on the page we need to start the conversion to * For unwritten space on the page we need to start the conversion to
* regular allocated space. * regular allocated space.
* 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. * 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 * 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 * 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 * or disk I/O during allocations is off, we need to fail the writepage and
* redirty the page. * redirty the page.
*
* The bh->b_state's cannot know if any of the blocks or which block for that
* matter are dirty due to mmap writes, and therefore bh uptodate is only
* valid if the page itself isn't completely uptodate.
*/ */
STATIC int STATIC int
xfs_vm_writepage( xfs_vm_writepage(
@ -1040,8 +1029,7 @@ xfs_vm_writepage(
struct writeback_control *wbc) struct writeback_control *wbc)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
int need_trans; int delalloc, unwritten;
int delalloc, unmapped, unwritten;
struct buffer_head *bh, *head; struct buffer_head *bh, *head;
struct xfs_bmbt_irec imap; struct xfs_bmbt_irec imap;
xfs_ioend_t *ioend = NULL, *iohead = NULL; xfs_ioend_t *ioend = NULL, *iohead = NULL;
@ -1052,10 +1040,12 @@ xfs_vm_writepage(
ssize_t size, len; ssize_t size, len;
int flags, err, imap_valid = 0, uptodate = 1; int flags, err, imap_valid = 0, uptodate = 1;
int count = 0; int count = 0;
int all_bh; int all_bh = 0;
trace_xfs_writepage(inode, page, 0); trace_xfs_writepage(inode, page, 0);
ASSERT(page_has_buffers(page));
/* /*
* Refuse to write the page out if we are called from reclaim context. * Refuse to write the page out if we are called from reclaim context.
* *
@ -1072,29 +1062,15 @@ xfs_vm_writepage(
goto out_fail; goto out_fail;
/* /*
* We need a transaction if: * We need a transaction if there are delalloc or unwritten buffers
* 1. There are delalloc buffers on the page * on the page.
* 2. The page is uptodate and we have unmapped buffers *
* 3. The page is uptodate and we have no buffers * If we need a transaction and the process flags say we are already
* 4. There are unwritten buffers on the page * in a transaction, or no IO is allowed then mark the page dirty
* again and leave the page as is.
*/ */
if (!page_has_buffers(page)) { xfs_count_page_state(page, &delalloc, &unwritten);
unmapped = 1; if ((current->flags & PF_FSTRANS) && (delalloc || unwritten))
need_trans = 1;
} else {
xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
if (!PageUptodate(page))
unmapped = 0;
need_trans = delalloc + unmapped + unwritten;
}
/*
* 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_test_flags(PF_FSTRANS) && need_trans)
goto out_fail; goto out_fail;
/* /*
@ -1117,7 +1093,8 @@ xfs_vm_writepage(
} }
end_offset = min_t(unsigned long long, end_offset = min_t(unsigned long long,
(xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT, offset); (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT,
offset);
len = 1 << inode->i_blkbits; len = 1 << inode->i_blkbits;
bh = head = page_buffers(page); bh = head = page_buffers(page);
@ -1125,8 +1102,6 @@ xfs_vm_writepage(
flags = BMAPI_READ; flags = BMAPI_READ;
type = IO_NEW; type = IO_NEW;
all_bh = unmapped;
do { do {
if (offset >= end_offset) if (offset >= end_offset)
break; break;
@ -1146,19 +1121,7 @@ xfs_vm_writepage(
if (imap_valid) if (imap_valid)
imap_valid = xfs_imap_valid(inode, &imap, offset); imap_valid = xfs_imap_valid(inode, &imap, offset);
/* if (buffer_unwritten(bh) || buffer_delay(bh)) {
* First case, map an unwritten extent and prepare for
* extent state conversion transaction on completion.
*
* Second case, allocate space for a delalloc buffer.
* We can return EAGAIN here in the release page case.
*
* Third case, an unmapped buffer was found, and we are
* in a path where we need to write the whole page out.
*/
if (buffer_unwritten(bh) || buffer_delay(bh) ||
((buffer_uptodate(bh) || PageUptodate(page)) &&
!buffer_mapped(bh))) {
int new_ioend = 0; int new_ioend = 0;
/* /*
@ -1177,14 +1140,11 @@ xfs_vm_writepage(
if (wbc->sync_mode == WB_SYNC_NONE && if (wbc->sync_mode == WB_SYNC_NONE &&
wbc->nonblocking) wbc->nonblocking)
flags |= BMAPI_TRYLOCK; flags |= BMAPI_TRYLOCK;
} else {
type = IO_NEW;
flags = BMAPI_WRITE | BMAPI_MMAP;
} }
if (!imap_valid) { if (!imap_valid) {
/* /*
* if we didn't have a valid mapping then we * If we didn't have a valid mapping then we
* need to ensure that we put the new mapping * need to ensure that we put the new mapping
* in a new ioend structure. This needs to be * in a new ioend structure. This needs to be
* done to ensure that the ioends correctly * done to ensure that the ioends correctly
@ -1192,14 +1152,7 @@ xfs_vm_writepage(
* for unwritten extent conversion. * for unwritten extent conversion.
*/ */
new_ioend = 1; new_ioend = 1;
if (type == IO_NEW) { err = xfs_map_blocks(inode, offset, len,
size = xfs_probe_cluster(inode,
page, bh, head, 0);
} else {
size = len;
}
err = xfs_map_blocks(inode, offset, size,
&imap, flags); &imap, flags);
if (err) if (err)
goto error; goto error;
@ -1220,8 +1173,7 @@ xfs_vm_writepage(
*/ */
if (!imap_valid || flags != BMAPI_READ) { if (!imap_valid || flags != BMAPI_READ) {
flags = BMAPI_READ; flags = BMAPI_READ;
size = xfs_probe_cluster(inode, page, bh, size = xfs_probe_cluster(inode, page, bh, head);
head, 1);
err = xfs_map_blocks(inode, offset, size, err = xfs_map_blocks(inode, offset, size,
&imap, flags); &imap, flags);
if (err) if (err)
@ -1240,7 +1192,6 @@ xfs_vm_writepage(
*/ */
type = IO_NEW; type = IO_NEW;
if (trylock_buffer(bh)) { if (trylock_buffer(bh)) {
ASSERT(buffer_mapped(bh));
if (imap_valid) if (imap_valid)
all_bh = 1; all_bh = 1;
xfs_add_to_ioend(inode, bh, offset, type, xfs_add_to_ioend(inode, bh, offset, type,
@ -1250,6 +1201,7 @@ xfs_vm_writepage(
imap_valid = 0; imap_valid = 0;
} }
} else if (PageUptodate(page)) { } else if (PageUptodate(page)) {
ASSERT(buffer_mapped(bh));
imap_valid = 0; imap_valid = 0;
} }
@ -1291,8 +1243,7 @@ error:
if (iohead) if (iohead)
xfs_cancel_ioend(iohead); xfs_cancel_ioend(iohead);
if (!unmapped) xfs_aops_discard_page(page);
xfs_aops_discard_page(page);
ClearPageUptodate(page); ClearPageUptodate(page);
unlock_page(page); unlock_page(page);
return err; return err;
@ -1324,11 +1275,11 @@ xfs_vm_releasepage(
struct page *page, struct page *page,
gfp_t gfp_mask) gfp_t gfp_mask)
{ {
int delalloc, unmapped, unwritten; int delalloc, unwritten;
trace_xfs_releasepage(page->mapping->host, page, 0); trace_xfs_releasepage(page->mapping->host, page, 0);
xfs_count_page_state(page, &delalloc, &unmapped, &unwritten); xfs_count_page_state(page, &delalloc, &unwritten);
if (WARN_ON(delalloc)) if (WARN_ON(delalloc))
return 0; return 0;

View File

@ -45,6 +45,6 @@ extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int);
extern void xfs_ioend_init(void); extern void xfs_ioend_init(void);
extern void xfs_ioend_wait(struct xfs_inode *); extern void xfs_ioend_wait(struct xfs_inode *);
extern void xfs_count_page_state(struct page *, int *, int *, int *); extern void xfs_count_page_state(struct page *, int *, int *);
#endif /* __XFS_AOPS_H__ */ #endif /* __XFS_AOPS_H__ */

View File

@ -832,33 +832,29 @@ DECLARE_EVENT_CLASS(xfs_page_class,
__field(loff_t, size) __field(loff_t, size)
__field(unsigned long, offset) __field(unsigned long, offset)
__field(int, delalloc) __field(int, delalloc)
__field(int, unmapped)
__field(int, unwritten) __field(int, unwritten)
), ),
TP_fast_assign( TP_fast_assign(
int delalloc = -1, unmapped = -1, unwritten = -1; int delalloc = -1, unwritten = -1;
if (page_has_buffers(page)) if (page_has_buffers(page))
xfs_count_page_state(page, &delalloc, xfs_count_page_state(page, &delalloc, &unwritten);
&unmapped, &unwritten);
__entry->dev = inode->i_sb->s_dev; __entry->dev = inode->i_sb->s_dev;
__entry->ino = XFS_I(inode)->i_ino; __entry->ino = XFS_I(inode)->i_ino;
__entry->pgoff = page_offset(page); __entry->pgoff = page_offset(page);
__entry->size = i_size_read(inode); __entry->size = i_size_read(inode);
__entry->offset = off; __entry->offset = off;
__entry->delalloc = delalloc; __entry->delalloc = delalloc;
__entry->unmapped = unmapped;
__entry->unwritten = unwritten; __entry->unwritten = unwritten;
), ),
TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx " TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx "
"delalloc %d unmapped %d unwritten %d", "delalloc %d unwritten %d",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino, __entry->ino,
__entry->pgoff, __entry->pgoff,
__entry->size, __entry->size,
__entry->offset, __entry->offset,
__entry->delalloc, __entry->delalloc,
__entry->unmapped,
__entry->unwritten) __entry->unwritten)
) )