@@ -1593,12 +1593,24 @@ iomap_finish_ioends(struct iomap_ioend *ioend, int error)
}
EXPORT_SYMBOL_GPL(iomap_finish_ioends);
+/*
+ * Calculate the physical size of an ioend by rounding up to block granularity.
+ * io_size might be unaligned if the last block crossed an unaligned i_size
+ * boundary at creation time.
+ */
+static inline size_t iomap_ioend_size_aligned(struct iomap_ioend *ioend)
+{
+ return round_up(ioend->io_size, i_blocksize(ioend->io_inode));
+}
+
/*
* We can merge two adjacent ioends if they have the same set of work to do.
*/
static bool
iomap_ioend_can_merge(struct iomap_ioend *ioend, struct iomap_ioend *next)
{
+ size_t size = iomap_ioend_size_aligned(ioend);
+
if (ioend->io_bio.bi_status != next->io_bio.bi_status)
return false;
if (next->io_flags & IOMAP_F_BOUNDARY)
@@ -1609,7 +1621,7 @@ iomap_ioend_can_merge(struct iomap_ioend *ioend, struct iomap_ioend *next)
if ((ioend->io_type == IOMAP_UNWRITTEN) ^
(next->io_type == IOMAP_UNWRITTEN))
return false;
- if (ioend->io_offset + ioend->io_size != next->io_offset)
+ if (ioend->io_offset + size != next->io_offset)
return false;
/*
* Do not merge physically discontiguous ioends. The filesystem
@@ -1621,7 +1633,7 @@ iomap_ioend_can_merge(struct iomap_ioend *ioend, struct iomap_ioend *next)
* submission so does not point to the start sector of the bio at
* completion.
*/
- if (ioend->io_sector + (ioend->io_size >> 9) != next->io_sector)
+ if (ioend->io_sector + (size >> 9) != next->io_sector)
return false;
return true;
}
@@ -1638,7 +1650,8 @@ iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends)
if (!iomap_ioend_can_merge(ioend, next))
break;
list_move_tail(&next->io_list, &ioend->io_list);
- ioend->io_size += next->io_size;
+ ioend->io_size = iomap_ioend_size_aligned(ioend) +
+ next->io_size;
}
}
EXPORT_SYMBOL_GPL(iomap_ioend_try_merge);
@@ -1742,7 +1755,7 @@ static bool iomap_can_add_to_ioend(struct iomap_writepage_ctx *wpc, loff_t pos)
return false;
if (wpc->iomap.type != wpc->ioend->io_type)
return false;
- if (pos != wpc->ioend->io_offset + wpc->ioend->io_size)
+ if (pos != wpc->ioend->io_offset + iomap_ioend_size_aligned(wpc->ioend))
return false;
if (iomap_sector(&wpc->iomap, pos) !=
bio_end_sector(&wpc->ioend->io_bio))
@@ -1774,6 +1787,8 @@ static int iomap_add_to_ioend(struct iomap_writepage_ctx *wpc,
{
struct iomap_folio_state *ifs = folio->private;
size_t poff = offset_in_folio(folio, pos);
+ loff_t isize = i_size_read(inode);
+ struct iomap_ioend *ioend;
int error;
if (!wpc->ioend || !iomap_can_add_to_ioend(wpc, pos)) {
@@ -1784,12 +1799,22 @@ static int iomap_add_to_ioend(struct iomap_writepage_ctx *wpc,
wpc->ioend = iomap_alloc_ioend(wpc, wbc, inode, pos);
}
- if (!bio_add_folio(&wpc->ioend->io_bio, folio, len, poff))
+ ioend = wpc->ioend;
+ if (!bio_add_folio(&ioend->io_bio, folio, len, poff))
goto new_ioend;
if (ifs)
atomic_add(len, &ifs->write_bytes_pending);
- wpc->ioend->io_size += len;
+
+ /*
+ * If the ioend spans i_size, trim io_size to the former to provide
+ * the fs with more accurate size information. This is useful for
+ * completion time on-disk size updates.
+ */
+ ioend->io_size = iomap_ioend_size_aligned(ioend) + len;
+ if (ioend->io_offset + ioend->io_size > isize)
+ ioend->io_size = isize - ioend->io_offset;
+
wbc_account_cgroup_owner(wbc, folio, len);
return 0;
}
@@ -335,7 +335,7 @@ struct iomap_ioend {
u16 io_type;
u16 io_flags; /* IOMAP_F_* */
struct inode *io_inode; /* file being written to */
- size_t io_size; /* size of the extent */
+ size_t io_size; /* size of data within eof */
loff_t io_offset; /* offset in the file */
sector_t io_sector; /* start sector of ioend */
struct bio io_bio; /* MUST BE LAST! */