From e878b35d3d36feb9cc7850af2ec508e7b403f8f4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Tue, 4 Feb 2025 17:14:23 -0800 Subject: [PATCH] ANDROID: fuse: Adjust Passthrough for writeback cache This adjusts some tracking to allow passthrough and writeback cache to co-exist with eachother Change-Id: I5b8b3940a8c096cd3598d7c188439c6c636c01a1 Bug: 372951405 Fixes: 3ae410445fb1 ("ANDROID: fuse: Allow passthrough and not passthrough") test: atest android.scopedstorage.cts.general.ScopedStorageDeviceTest Signed-off-by: Daniel Rosenberg --- fs/fuse/file.c | 1 + fs/fuse/fuse_i.h | 5 ++++- fs/fuse/inode.c | 1 + fs/fuse/iomode.c | 41 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 614b3707c063..68d43659d8b9 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3539,6 +3539,7 @@ void fuse_init_file_inode(struct inode *inode, unsigned int flags) INIT_LIST_HEAD(&fi->queued_writes); fi->writectr = 0; fi->iocachectr = 0; + fi->iopassctr = 0; init_waitqueue_head(&fi->page_waitq); init_waitqueue_head(&fi->direct_io_waitq); fi->writepages = RB_ROOT; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 621e0cde1067..810eda2ebf1f 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -195,6 +195,9 @@ struct fuse_inode { /** Number of files/maps using page cache */ int iocachectr; + /* Number of files using passthrough */ + int iopassctr; + /* Waitq for writepage completion */ wait_queue_head_t page_waitq; @@ -328,7 +331,7 @@ struct fuse_file { wait_queue_head_t poll_wait; /** Does file hold a fi->iocachectr refcount? */ - enum { IOM_NONE, IOM_CACHED, IOM_UNCACHED } iomode; + enum { IOM_NONE, IOM_CACHED, IOM_UNCACHED, IOM_PASSTHROUGH } iomode; #ifdef CONFIG_FUSE_PASSTHROUGH /** Reference to backing file in passthrough mode */ diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7187c58dfeb6..1d003e300c52 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -184,6 +184,7 @@ static void fuse_evict_inode(struct inode *inode) } if (S_ISREG(inode->i_mode) && !fuse_is_bad(inode)) { WARN_ON(fi->iocachectr != 0); + WARN_ON(fi->iopassctr != 0); WARN_ON(!list_empty(&fi->write_files)); WARN_ON(!list_empty(&fi->queued_writes)); } diff --git a/fs/fuse/iomode.c b/fs/fuse/iomode.c index 7dc0233ae632..47cc3e75c01d 100644 --- a/fs/fuse/iomode.c +++ b/fs/fuse/iomode.c @@ -62,6 +62,7 @@ int fuse_file_cached_io_open(struct inode *inode, struct fuse_file *ff) #endif WARN_ON(ff->iomode == IOM_UNCACHED); + WARN_ON(ff->iomode == IOM_PASSTHROUGH); if (ff->iomode == IOM_NONE) { ff->iomode = IOM_CACHED; if (fi->iocachectr == 0) @@ -85,7 +86,7 @@ static void fuse_file_cached_io_release(struct fuse_file *ff, spin_unlock(&fi->lock); } -/* Start strictly uncached io mode where cache access is not allowed */ +/* Start strictly uncached io mode where cache access is not allowed if not in passthrough mode */ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb) { struct fuse_backing *oldfb; @@ -98,11 +99,14 @@ int fuse_inode_uncached_io_start(struct fuse_inode *fi, struct fuse_backing *fb) err = -EBUSY; goto unlock; } - if (fi->iocachectr > 0) { + if (!fb && fi->iocachectr > 0) { err = -ETXTBSY; goto unlock; } - fi->iocachectr--; + if (fb) + fi->iopassctr++; + else + fi->iocachectr--; /* fuse inode holds a single refcount of backing file */ if (fb && !oldfb) { @@ -129,7 +133,7 @@ static int fuse_file_uncached_io_open(struct inode *inode, return err; WARN_ON(ff->iomode != IOM_NONE); - ff->iomode = IOM_UNCACHED; + ff->iomode = IOM_PASSTHROUGH; return 0; } @@ -158,6 +162,30 @@ static void fuse_file_uncached_io_release(struct fuse_file *ff, fuse_inode_uncached_io_end(fi); } +static void fuse_inode_passthrough_io_end(struct fuse_inode *fi) +{ + struct fuse_backing *oldfb = NULL; + + spin_lock(&fi->lock); + WARN_ON(fi->iopassctr == 0); + fi->iopassctr--; + if (!fi->iopassctr) { + oldfb = fuse_inode_backing_set(fi, NULL); + } + spin_unlock(&fi->lock); + if (oldfb) + fuse_backing_put(oldfb); +} + +/* Drop uncached_io reference from passthrough open */ +static void fuse_file_passthrough_io_release(struct fuse_file *ff, + struct fuse_inode *fi) +{ + WARN_ON(ff->iomode != IOM_PASSTHROUGH); + ff->iomode = IOM_NONE; + fuse_inode_passthrough_io_end(fi); +} + /* * Open flags that are allowed in combination with FOPEN_PASSTHROUGH. * A combination of FOPEN_PASSTHROUGH and FOPEN_DIRECT_IO means that read/write @@ -167,7 +195,7 @@ static void fuse_file_uncached_io_release(struct fuse_file *ff, */ #define FOPEN_PASSTHROUGH_MASK \ (FOPEN_PASSTHROUGH | FOPEN_DIRECT_IO | FOPEN_PARALLEL_DIRECT_WRITES | \ - FOPEN_NOFLUSH) + FOPEN_NOFLUSH | FOPEN_KEEP_CACHE) static int fuse_file_passthrough_open(struct inode *inode, struct file *file) { @@ -277,5 +305,8 @@ void fuse_file_io_release(struct fuse_file *ff, struct inode *inode) case IOM_CACHED: fuse_file_cached_io_release(ff, fi); break; + case IOM_PASSTHROUGH: + fuse_file_passthrough_io_release(ff, fi); + break; } }