diff options
| author | Joanne Koong <joannelkoong@gmail.com> | 2026-09-15 18:05:25 -0700 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@redhat.com> | 2026-09-25 13:00:37 +0200 |
| commit | d1c723c516dc2d43b3a5d668839da21c466953f4 (patch) | |
| tree | e2cd4747761dcc8129f932529407cf68afafcfb7 /tools/lib/python/kdoc/python_version.py | |
| parent | ad866d756de0682419b04d66b466046880482f03 (diff) | |
fuse: drop rather than zero the copied range in copy_file_range()
After a FUSE_COPY_FILE_RANGE the destination page cache holds stale data,
so __fuse_copy_file_range() clears it with truncate_inode_pages_range()
over a page aligned range.
truncate_inode_pages_range() is built for ranges that are meant to read
back as zeros. A folio that the range only partially covers is handed to
truncate_inode_partial_folio(), which zeroes the covered bytes and then
tries to split the folio so that the covered part can be dropped. Today
every folio is a single page, and a page aligned range is therefore also
folio aligned, so every folio in the range is covered in full and nothing
is ever zeroed.
Once folios can be larger than a page, the range may not line up with
folios (eg the folio holding the start of the range can begin before it
and the folio holding the end can go beyond it). In this case, only the
copied ranges inside those folios will get zeroed. If blocks within
those folios are dirty, then splitting returns -EBUSY and
truncate_inode_partial_folio() returns false. The folio stays in the
page cache with the copied range zeroed, and those zeros are written
back to the server, which overwrites the previously copied file range.
__fuse_copy_file_range() issues a synchronous writeback call before
calling truncate_inode_pages_range(), so in most cases the folio is
not dirty, but there is still the possibility that a memory map raced
with the copy (which is the case the comment in the code already calls
out).
Instead of calling truncate_inode_pages_range(), use
invalidate_inode_pages2_range(). It works in whole folios and never
zeroes, and fuse_writeback_range() has already run on the destination, so
the folios are normally clean and are simply dropped. It is also what the
rest of fuse already uses when the cache has to be re-read from the
server.
Outside the mmap race, the two are equivalent over the copied range for
order-0 folios. Regarding the mmap race however, the old call
(truncate_inode_pages_range()) loses the mmap changes but preserves
the copied ones, whereas the new code (invalidate_inode_pages2_range())
launders the folio before dropping it, which means it loses the copied
changes but preserves the mmap ones. Neither is entirely correct, and
the (nontrivial) fix for both is the mapping->invalidate_lock described
in the comment.
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'tools/lib/python/kdoc/python_version.py')
0 files changed, 0 insertions, 0 deletions
