|
|
@@ -36,6 +36,18 @@
|
|
|
#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,
|
|
|
@@ -335,7 +347,7 @@ xfs_map_blocks(
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-STATIC int
|
|
|
+STATIC bool
|
|
|
xfs_imap_valid(
|
|
|
struct inode *inode,
|
|
|
struct xfs_bmbt_irec *imap,
|
|
|
@@ -532,29 +544,27 @@ xfs_add_to_ioend(
|
|
|
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
|
|
|
@@ -651,17 +661,15 @@ xfs_convert_page(
|
|
|
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;
|
|
|
@@ -671,7 +679,7 @@ xfs_convert_page(
|
|
|
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;
|
|
|
|
|
|
/*
|
|
|
@@ -707,7 +715,7 @@ xfs_convert_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;
|
|
|
@@ -739,23 +747,22 @@ xfs_convert_page(
|
|
|
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++;
|
|
|
@@ -790,8 +797,7 @@ STATIC void
|
|
|
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)
|
|
|
{
|
|
|
@@ -807,7 +813,7 @@ xfs_cluster_write(
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
@@ -895,21 +901,20 @@ out_invalidate:
|
|
|
|
|
|
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;
|
|
|
@@ -924,20 +929,19 @@ xfs_writepage_submit(
|
|
|
* 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);
|
|
|
@@ -1036,11 +1040,8 @@ xfs_vm_writepage(
|
|
|
|
|
|
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))
|
|
|
@@ -1053,24 +1054,24 @@ xfs_vm_writepage(
|
|
|
* 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))
|
|
|
@@ -1081,38 +1082,29 @@ xfs_vm_writepage(
|
|
|
* 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));
|
|
|
|
|
|
@@ -1122,10 +1114,10 @@ xfs_vm_writepage(
|
|
|
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);
|
|
|
|
|
|
/*
|
|
|
@@ -1133,10 +1125,10 @@ xfs_vm_writepage(
|
|
|
* 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;
|
|
|
@@ -1148,32 +1140,30 @@ xfs_vm_writepage(
|
|
|
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);
|
|
|
@@ -1187,13 +1177,33 @@ redirty:
|
|
|
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);
|
|
|
}
|
|
|
|
|
|
/*
|