ANDROID: Fuse: Fix Passthrough and Cache Coherency
Fuse writeback and passthrough have some interactions that require additional handling. If there is cached data from previous reads, and a passthrough write happens, the relevant sections of the cache should be invalidated. Otherwise the new data is inaccessible to non-passthrough until the cache otherwise expires. If there are dirty pages, passthrough read would not be aware of these changes. First, they must be flushed so that the lower filesystem will reflect the change. This can't be done directly since there may be specific logic about how that data gets written that fuse itself does not know about. This also enables mmap for passthrough and nonpassthrough at the same time. Note that if a shared mmap is open for both passthrough and nonpassthrough, there will be some inconsistencies between data seen on both, although interactions between file read and write are handled. This is an improvement on the existing Android interaction. Change-Id: I45c3efe92849a88ffcf586a1ec6508fed632e1d0 Bug: 372951405 Test: atest android.scopedstorage.cts.general.ScopedStorageDeviceTest Signed-off-by: Daniel Rosenberg <drosen@google.com>
This commit is contained in:
@@ -2701,8 +2701,16 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
*/
|
||||
if (fuse_file_passthrough(ff))
|
||||
return fuse_passthrough_mmap(file, vma);
|
||||
/*
|
||||
* Old Android passthrough did not handle this case, but did allow the mmap to continue.
|
||||
* This will not cleanly handle the case of a shared mmap across passthrough and
|
||||
* nonpassthrough at the same time, although shared mmap through cache and file io through
|
||||
* the lower filesystem should work as expected, at a performance penalty.
|
||||
*/
|
||||
#if 0
|
||||
else if (fuse_inode_backing(get_fuse_inode(inode)))
|
||||
return -ENODEV;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* FOPEN_DIRECT_IO handling is special compared to O_DIRECT,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/file.h>
|
||||
#include <linux/backing-file.h>
|
||||
#include <linux/splice.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
||||
static void fuse_file_accessed(struct file *file)
|
||||
{
|
||||
@@ -34,6 +35,10 @@ static void fuse_passthrough_end_write(struct file *file, loff_t pos, ssize_t re
|
||||
inode->i_blocks = backing_inode->i_blocks;
|
||||
i_size_write(inode, i_size_read(backing_inode));
|
||||
}
|
||||
if (ret > 0) {
|
||||
invalidate_inode_pages2_range(inode->i_mapping,
|
||||
(pos - ret) >> PAGE_SHIFT, pos >> PAGE_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
@@ -56,6 +61,8 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter)
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
/* Flush any dirtied cache pages from fuse cache */
|
||||
write_inode_now(file_inode(file), 1);
|
||||
ret = backing_file_read_iter(backing_file, iter, iocb, iocb->ki_flags,
|
||||
&ctx);
|
||||
|
||||
@@ -106,6 +113,8 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
|
||||
pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
|
||||
backing_file, ppos ? *ppos : 0, len, flags);
|
||||
|
||||
/* Flush any dirtied cache pages from fuse cache */
|
||||
write_inode_now(file_inode(in), 1);
|
||||
return backing_file_splice_read(backing_file, ppos, pipe, len, flags,
|
||||
&ctx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user