diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 33d882c2ad4d5e0730e82ffca7c61f284fe91f87..ef8a8345ca9a091b622cf0f8b1c68ce7ca41b9a5 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1846,6 +1846,7 @@ 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); int error; if (!wpc->ioend || !iomap_can_add_to_ioend(wpc, pos)) { @@ -1861,7 +1862,54 @@ static int iomap_add_to_ioend(struct iomap_writepage_ctx *wpc, if (ifs) atomic_add(len, &ifs->write_bytes_pending); + + /* + * Clamp io_offset and io_size to the incore EOF so that ondisk + * file size updates in the ioend completion are byte-accurate. + * This avoids recovering files with zeroed tail regions when + * writeback races with appending writes: + * + * Thread 1: Thread 2: + * ------------ ----------- + * write [A, A+B] + * update inode size to A+B + * submit I/O [A, A+BS] + * write [A+B, A+B+C] + * update inode size to A+B+C + * + * + * + * After reboot: + * 1) with A+B+C < A+BS, the file has zero padding in range + * [A+B, A+B+C] + * + * |< Block Size (BS) >| + * |DDDDDDDDDDDD0000000000000| + * ^ ^ ^ + * A A+B A+B+C + * (EOF) + * + * 2) with A+B+C > A+BS, the file has zero padding in range + * [A+B, A+BS] + * + * |< Block Size (BS) >|< Block Size (BS) >| + * |DDDDDDDDDDDD0000000000000|00000000000000000000000000| + * ^ ^ ^ ^ + * A A+B A+BS A+B+C + * (EOF) + * + * D = Valid Data + * 0 = Zero Padding + * + * Note that this defeats the ability to chain the ioends of + * appending writes. Writeback beyond EOF block may occur in + * concurrent scenarios(e.g. racing with truncate) and io_size + * should not be trimmed in such cases. + */ wpc->ioend->io_size += len; + if (pos < isize && pos + len > isize) + wpc->ioend->io_size = isize - wpc->ioend->io_offset; + wbc_account_cgroup_owner(wbc, &folio->page, len); return 0; } diff --git a/include/linux/iomap.h b/include/linux/iomap.h index fd584156253fd2acc643d8d4b4b47555ee926405..6bd7ed98cf1f45cc5e11de09a2462790cf47a0a7 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -318,7 +318,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! */