#include <linux/pagevec.h>
#include <linux/writeback.h>
+/*
+ * structure owned by writepages passed to individual writepage calls
+ */
+struct xfs_writepage_ctx {
+ struct xfs_bmbt_irec imap;
+ bool imap_valid;
+ unsigned int io_type;
+ struct xfs_ioend *iohead;
+ struct xfs_ioend *ioend;
+ sector_t last_block;
+};
+
void
xfs_count_page_state(
struct page *page,
return 0;
}
-STATIC int
+STATIC bool
xfs_imap_valid(
struct inode *inode,
struct xfs_bmbt_irec *imap,
struct inode *inode,
struct buffer_head *bh,
xfs_off_t offset,
- unsigned int type,
- xfs_ioend_t **result,
- int need_ioend)
+ struct xfs_writepage_ctx *wpc)
{
- xfs_ioend_t *ioend = *result;
-
- if (!ioend || need_ioend || type != ioend->io_type) {
- xfs_ioend_t *previous = *result;
-
- ioend = xfs_alloc_ioend(inode, type);
- ioend->io_offset = offset;
- ioend->io_buffer_head = bh;
- ioend->io_buffer_tail = bh;
- if (previous)
- previous->io_list = ioend;
- *result = ioend;
+ if (!wpc->ioend || wpc->io_type != wpc->ioend->io_type ||
+ bh->b_blocknr != wpc->last_block + 1) {
+ struct xfs_ioend *new;
+
+ new = xfs_alloc_ioend(inode, wpc->io_type);
+ new->io_offset = offset;
+ new->io_buffer_head = bh;
+ new->io_buffer_tail = bh;
+ if (wpc->ioend)
+ wpc->ioend->io_list = new;
+ wpc->ioend = new;
} else {
- ioend->io_buffer_tail->b_private = bh;
- ioend->io_buffer_tail = bh;
+ wpc->ioend->io_buffer_tail->b_private = bh;
+ wpc->ioend->io_buffer_tail = bh;
}
bh->b_private = NULL;
- ioend->io_size += bh->b_size;
+ wpc->ioend->io_size += bh->b_size;
+ wpc->last_block = bh->b_blocknr;
}
STATIC void
struct inode *inode,
struct page *page,
loff_t tindex,
- struct xfs_bmbt_irec *imap,
- xfs_ioend_t **ioendp,
+ struct xfs_writepage_ctx *wpc,
struct writeback_control *wbc)
{
struct buffer_head *bh, *head;
xfs_off_t end_offset;
unsigned long p_offset;
- unsigned int type;
int len, page_dirty;
int count = 0, done = 0, uptodate = 1;
- xfs_off_t offset = page_offset(page);
+ xfs_off_t offset = page_offset(page);
if (page->index != tindex)
goto fail;
goto fail_unlock_page;
if (page->mapping != inode->i_mapping)
goto fail_unlock_page;
- if (!xfs_check_page_type(page, (*ioendp)->io_type, false))
+ if (!xfs_check_page_type(page, wpc->ioend->io_type, false))
goto fail_unlock_page;
/*
* writeback. Hence for more optimal IO patterns, we should always
* avoid partial page writeback due to multiple mappings on a page here.
*/
- if (!xfs_imap_valid(inode, imap, end_offset))
+ if (!xfs_imap_valid(inode, &wpc->imap, end_offset))
goto fail_unlock_page;
len = 1 << inode->i_blkbits;
if (buffer_unwritten(bh) || buffer_delay(bh) ||
buffer_mapped(bh)) {
if (buffer_unwritten(bh))
- type = XFS_IO_UNWRITTEN;
+ wpc->io_type = XFS_IO_UNWRITTEN;
else if (buffer_delay(bh))
- type = XFS_IO_DELALLOC;
+ wpc->io_type = XFS_IO_DELALLOC;
else
- type = XFS_IO_OVERWRITE;
+ wpc->io_type = XFS_IO_OVERWRITE;
/*
* imap should always be valid because of the above
* partial page end_offset check on the imap.
*/
- ASSERT(xfs_imap_valid(inode, imap, offset));
+ ASSERT(xfs_imap_valid(inode, &wpc->imap, offset));
lock_buffer(bh);
- if (type != XFS_IO_OVERWRITE)
- xfs_map_at_offset(inode, bh, imap, offset);
- xfs_add_to_ioend(inode, bh, offset, type,
- ioendp, done);
+ if (wpc->io_type != XFS_IO_OVERWRITE)
+ xfs_map_at_offset(inode, bh, &wpc->imap, offset);
+ xfs_add_to_ioend(inode, bh, offset, wpc);
page_dirty--;
count++;
xfs_cluster_write(
struct inode *inode,
pgoff_t tindex,
- struct xfs_bmbt_irec *imap,
- xfs_ioend_t **ioendp,
+ struct xfs_writepage_ctx *wpc,
struct writeback_control *wbc,
pgoff_t tlast)
{
for (i = 0; i < pagevec_count(&pvec); i++) {
done = xfs_convert_page(inode, pvec.pages[i], tindex++,
- imap, ioendp, wbc);
+ wpc, wbc);
if (done)
break;
}
static int
xfs_writepage_submit(
- struct xfs_ioend *ioend,
- struct xfs_ioend *iohead,
+ struct xfs_writepage_ctx *wpc,
struct writeback_control *wbc,
int status)
{
struct blk_plug plug;
/* Reserve log space if we might write beyond the on-disk inode size. */
- if (!status && ioend && ioend->io_type != XFS_IO_UNWRITTEN &&
- xfs_ioend_is_append(ioend))
- status = xfs_setfilesize_trans_alloc(ioend);
+ if (!status && wpc->ioend && wpc->ioend->io_type != XFS_IO_UNWRITTEN &&
+ xfs_ioend_is_append(wpc->ioend))
+ status = xfs_setfilesize_trans_alloc(wpc->ioend);
- if (iohead) {
+ if (wpc->iohead) {
blk_start_plug(&plug);
- xfs_submit_ioend(wbc, iohead, status);
+ xfs_submit_ioend(wbc, wpc->iohead, status);
blk_finish_plug(&plug);
}
return status;
* For any other dirty buffer heads on the page we should flush them.
*/
STATIC int
-xfs_vm_writepage(
+xfs_do_writepage(
struct page *page,
- struct writeback_control *wbc)
+ struct writeback_control *wbc,
+ void *data)
{
+ struct xfs_writepage_ctx *wpc = data;
struct inode *inode = page->mapping->host;
struct buffer_head *bh, *head;
- struct xfs_bmbt_irec imap;
- xfs_ioend_t *ioend = NULL, *iohead = NULL;
loff_t offset;
- unsigned int type;
__uint64_t end_offset;
pgoff_t end_index, last_index;
ssize_t len;
- int err, imap_valid = 0, uptodate = 1;
+ int err, uptodate = 1;
int count = 0;
trace_xfs_writepage(inode, page, 0, 0);
bh = head = page_buffers(page);
offset = page_offset(page);
- type = XFS_IO_OVERWRITE;
do {
- int new_ioend = 0;
-
if (offset >= end_offset)
break;
if (!buffer_uptodate(bh))
* buffers covering holes here.
*/
if (!buffer_mapped(bh) && buffer_uptodate(bh)) {
- imap_valid = 0;
+ wpc->imap_valid = false;
continue;
}
if (buffer_unwritten(bh)) {
- if (type != XFS_IO_UNWRITTEN) {
- type = XFS_IO_UNWRITTEN;
- imap_valid = 0;
+ if (wpc->io_type != XFS_IO_UNWRITTEN) {
+ wpc->io_type = XFS_IO_UNWRITTEN;
+ wpc->imap_valid = false;
}
} else if (buffer_delay(bh)) {
- if (type != XFS_IO_DELALLOC) {
- type = XFS_IO_DELALLOC;
- imap_valid = 0;
+ if (wpc->io_type != XFS_IO_DELALLOC) {
+ wpc->io_type = XFS_IO_DELALLOC;
+ wpc->imap_valid = false;
}
} else if (buffer_uptodate(bh)) {
- if (type != XFS_IO_OVERWRITE) {
- type = XFS_IO_OVERWRITE;
- imap_valid = 0;
+ if (wpc->io_type != XFS_IO_OVERWRITE) {
+ wpc->io_type = XFS_IO_OVERWRITE;
+ wpc->imap_valid = false;
}
} else {
if (PageUptodate(page))
* subsequent writeable buffers into a new
* ioend.
*/
- imap_valid = 0;
+ wpc->imap_valid = 0;
continue;
}
- if (imap_valid)
- imap_valid = xfs_imap_valid(inode, &imap, offset);
- if (!imap_valid) {
- /*
- * If we didn't have a valid mapping then we need to
- * put the new mapping into a separate ioend structure.
- * This ensures non-contiguous extents always have
- * separate ioends, which is particularly important
- * for unwritten extent conversion at I/O completion
- * time.
- */
- new_ioend = 1;
- err = xfs_map_blocks(inode, offset, &imap, type);
+ if (wpc->imap_valid)
+ wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
+ if (!wpc->imap_valid) {
+ err = xfs_map_blocks(inode, offset, &wpc->imap,
+ wpc->io_type);
if (err)
goto error;
- imap_valid = xfs_imap_valid(inode, &imap, offset);
+ wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
}
- if (imap_valid) {
+ if (wpc->imap_valid) {
lock_buffer(bh);
- if (type != XFS_IO_OVERWRITE)
- xfs_map_at_offset(inode, bh, &imap, offset);
- xfs_add_to_ioend(inode, bh, offset, type, &ioend,
- new_ioend);
+ if (wpc->io_type != XFS_IO_OVERWRITE)
+ xfs_map_at_offset(inode, bh, &wpc->imap, offset);
+ xfs_add_to_ioend(inode, bh, offset, wpc);
count++;
}
- if (!iohead)
- iohead = ioend;
+ if (!wpc->iohead)
+ wpc->iohead = wpc->ioend;
} while (offset += len, ((bh = bh->b_this_page) != head));
xfs_start_page_writeback(page, 1, count);
/* if there is no IO to be submitted for this page, we are done */
- if (!ioend)
+ if (!count)
return 0;
- ASSERT(iohead);
+ ASSERT(wpc->iohead);
ASSERT(err == 0);
/*
* completion path as we have marked the initial page as under writeback
* and unlocked it.
*/
- if (imap_valid) {
+ if (wpc->imap_valid) {
xfs_off_t end_index;
- end_index = imap.br_startoff + imap.br_blockcount;
+ end_index = wpc->imap.br_startoff + wpc->imap.br_blockcount;
/* to bytes */
end_index <<= inode->i_blkbits;
if (end_index > last_index)
end_index = last_index;
- xfs_cluster_write(inode, page->index + 1, &imap, &ioend,
- wbc, end_index);
+ xfs_cluster_write(inode, page->index + 1, wpc, wbc, end_index);
}
-
- return xfs_writepage_submit(ioend, iohead, wbc, 0);
+ return 0;
error:
/*
* On error, we have to fail the iohead here because we buffers locked
* in the ioend chain. If we don't do this, we'll deadlock invalidating
* the page as that tries to lock the buffers on the page. Also, because
- * we may have set pages under writeback, we have to run IO completion to
- * mark the error state of the IO appropriately, so we can't cancel the
- * ioend directly here. That means we have to mark this page as under
- * writeback if we included any buffers from it in the ioend chain.
+ * we may have set pages under writeback, we have to make sure we run
+ * IO completion to mark the error state of the IO appropriately, so we
+ * can't cancel the ioend directly here. That means we have to mark this
+ * page as under writeback if we included any buffers from it in the
+ * ioend chain so that completion treats it correctly.
+ *
+ * If we didn't include the page in the ioend, then we can simply
+ * discard and unlock it as there are no other users of the page or it's
+ * buffers right now. The caller will still need to trigger submission
+ * of outstanding ioends on the writepage context so they are treated
+ * correctly on error.
*/
if (count)
xfs_start_page_writeback(page, 0, count);
- xfs_writepage_submit(ioend, iohead, wbc, err);
-
- /*
- * We can only discard the page we had the IO error on if we haven't
- * included it in the ioend above. If it has already been errored out,
- * the it is unlocked and we can't touch it here.
- */
- if (!count) {
+ else {
xfs_aops_discard_page(page);
ClearPageUptodate(page);
unlock_page(page);
return 0;
}
+STATIC int
+xfs_vm_writepage(
+ struct page *page,
+ struct writeback_control *wbc)
+{
+ struct xfs_writepage_ctx wpc = {
+ .io_type = XFS_IO_INVALID,
+ };
+ int ret;
+
+ ret = xfs_do_writepage(page, wbc, &wpc);
+ return xfs_writepage_submit(&wpc, wbc, ret);
+}
+
STATIC int
xfs_vm_writepages(
struct address_space *mapping,
struct writeback_control *wbc)
{
+ struct xfs_writepage_ctx wpc = {
+ .io_type = XFS_IO_INVALID,
+ };
+ int ret;
+
xfs_iflags_clear(XFS_I(mapping->host), XFS_ITRUNCATED);
- return generic_writepages(mapping, wbc);
+ ret = write_cache_pages(mapping, wbc, xfs_do_writepage, &wpc);
+ return xfs_writepage_submit(&wpc, wbc, ret);
}
/*