A splice() into /dev/fuse with SPLICE_F_MOVE lets the reply to a read
request steal full pages from the pipe.  Each stolen page replaces, in
the fuse file's page cache, the folio that the request allocated.  The
page can be a page cache folio of another file, a page written into the
pipe, or a gifted user page.

Always copy.  The server sees the same data, and the move was a hint
that splice() never promised to honour.

Keep advertising FUSE_SPLICE_MOVE.  libfuse fails the mount of a
filesystem that asks for a capability the kernel does not offer.

Preparation for removing ->try_steal() from pipe buffers.

Suggested-by: David Hildenbrand (Arm) <[email protected]>
Suggested-by: Linus Torvalds <[email protected]>
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
---
 fs/fuse/args.h       |   1 -
 fs/fuse/dev.c        | 144 +------------------------------------------
 fs/fuse/file.c       |   1 -
 fs/fuse/fuse_dev_i.h |   1 -
 4 files changed, 3 insertions(+), 144 deletions(-)

diff --git a/fs/fuse/args.h b/fs/fuse/args.h
index 5173264a1261..36ef0c6d5999 100644
--- a/fs/fuse/args.h
+++ b/fs/fuse/args.h
@@ -36,7 +36,6 @@ struct fuse_args {
        bool user_pages:1;
        bool out_argvar:1;
        bool page_zeroing:1;
-       bool page_replace:1;
        bool may_block:1;
        bool is_ext:1;
        bool is_pinned:1;
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 3641abb90d6b..e7238eed21fb 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1099,128 +1099,6 @@ static int fuse_copy_do(struct fuse_copy_state *cs, 
void **val, unsigned *size)
        return ncpy;
 }
 
-static int fuse_check_folio(struct folio *folio)
-{
-       if (folio_mapped(folio) ||
-           folio->mapping != NULL ||
-           (folio->flags.f & PAGE_FLAGS_CHECK_AT_PREP &
-            ~(1 << PG_locked |
-              1 << PG_referenced |
-              1 << PG_lru |
-              1 << PG_active |
-              1 << PG_workingset |
-              1 << PG_reclaim |
-              1 << PG_waiters |
-              LRU_GEN_MASK | LRU_REFS_MASK))) {
-               dump_page(&folio->page, "fuse: trying to steal weird page");
-               return 1;
-       }
-       return 0;
-}
-
-/*
- * Attempt to steal a page from the splice() pipe and move it into the
- * pagecache. If successful, the pointer in @pagep will be updated. The
- * folio that was originally in @pagep will lose a reference and the new
- * folio returned in @pagep will carry a reference.
- */
-static int fuse_try_move_folio(struct fuse_copy_state *cs, struct folio 
**foliop)
-{
-       int err;
-       struct folio *oldfolio = *foliop;
-       struct folio *newfolio;
-       struct pipe_buffer *buf = cs->pipebufs;
-
-       folio_get(oldfolio);
-       err = unlock_request(cs->req);
-       if (err)
-               goto out_put_old;
-
-       fuse_copy_finish(cs);
-
-       err = pipe_buf_confirm(cs->pipe, buf);
-       if (err)
-               goto out_put_old;
-
-       BUG_ON(!cs->nr_segs);
-       cs->currbuf = buf;
-       cs->len = buf->len;
-       cs->pipebufs++;
-       cs->nr_segs--;
-
-       if (cs->len != folio_size(oldfolio))
-               goto out_fallback;
-
-       newfolio = page_folio(buf->page);
-
-       if (folio_test_large(newfolio))
-               goto out_fallback;
-
-       /*
-        * This is a new and locked page, it shouldn't be mapped or
-        * have any special flags on it
-        */
-       if (WARN_ON(folio_mapped(oldfolio)))
-               goto out_fallback;
-       if (WARN_ON(folio_has_private(oldfolio)))
-               goto out_fallback;
-       if (WARN_ON(folio_test_dirty(oldfolio) ||
-                               folio_test_writeback(oldfolio)))
-               goto out_fallback;
-       if (WARN_ON(folio_test_mlocked(oldfolio)))
-               goto out_fallback;
-
-       if (!pipe_buf_try_steal(cs->pipe, buf))
-               goto out_fallback;
-
-       folio_clear_uptodate(newfolio);
-       folio_clear_mappedtodisk(newfolio);
-
-       if (fuse_check_folio(newfolio) != 0)
-               goto out_fallback_unlock;
-
-       err = lock_request(cs->req);
-       if (err)
-               goto out_fallback_unlock;
-
-       replace_page_cache_folio(oldfolio, newfolio);
-
-       folio_get(newfolio);
-
-       if (!(buf->flags & PIPE_BUF_FLAG_LRU))
-               folio_add_lru(newfolio);
-
-       /*
-        * Release while we have extra ref on stolen page.  Otherwise
-        * anon_pipe_buf_release() might think the page can be reused.
-        */
-       pipe_buf_release(cs->pipe, buf);
-
-       *foliop = newfolio;
-       folio_unlock(oldfolio);
-       /* Drop ref for ap->pages[] array */
-       folio_put(oldfolio);
-       cs->len = 0;
-
-       err = 0;
-out_put_old:
-       /* Drop ref obtained in this function */
-       folio_put(oldfolio);
-       return err;
-
-out_fallback_unlock:
-       folio_unlock(newfolio);
-out_fallback:
-       cs->pg = buf->page;
-       cs->offset = buf->offset;
-
-       err = lock_request(cs->req);
-       if (!err)
-               err = 1;
-
-       goto out_put_old;
-}
-
 static int fuse_ref_folio(struct fuse_copy_state *cs, struct folio *folio,
                          unsigned offset, unsigned count)
 {
@@ -1275,16 +1153,9 @@ int fuse_copy_folio(struct fuse_copy_state *cs, struct 
folio **foliop,
                                return fuse_ref_folio(cs, folio, offset, count);
                        }
                } else if (!cs->len) {
-                       if (cs->move_folios && folio &&
-                           offset == 0 && count == folio_size(folio)) {
-                               err = fuse_try_move_folio(cs, foliop);
-                               if (err <= 0)
-                                       return err;
-                       } else {
-                               err = fuse_copy_fill(cs);
-                               if (err)
-                                       return err;
-                       }
+                       err = fuse_copy_fill(cs);
+                       if (err)
+                               return err;
                }
                if (folio) {
                        void *mapaddr = kmap_local_folio(folio, offset);
@@ -1920,9 +1791,6 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
                if (!smp_load_acquire(&fch->initialized) || !fch->connected)
                        goto copy_finish;
 
-               /* Don't try to move folios (yet) */
-               cs->move_folios = false;
-
                err = fuse_notify(fch->conn, oh.error, nbytes - sizeof(oh), cs);
                goto copy_finish;
        }
@@ -1966,8 +1834,6 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
        set_bit(FR_LOCKED, &req->flags);
        spin_unlock(&fpq->lock);
        cs->req = req;
-       if (!req->args->page_replace)
-               cs->move_folios = false;
 
        if (oh.error)
                err = nbytes != sizeof(oh) ? -EINVAL : 0;
@@ -2068,7 +1934,6 @@ static ssize_t fuse_dev_splice_write(struct 
pipe_inode_info *pipe,
                                goto out_free;
 
                        *obuf = *ibuf;
-                       obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
                        obuf->len = rem;
                        ibuf->offset += obuf->len;
                        ibuf->len -= obuf->len;
@@ -2083,9 +1948,6 @@ static ssize_t fuse_dev_splice_write(struct 
pipe_inode_info *pipe,
        cs.nr_segs = nbuf;
        cs.pipe = pipe;
 
-       if (flags & SPLICE_F_MOVE)
-               cs.move_folios = true;
-
        ret = fuse_dev_do_write(fud, &cs, len);
 
        pipe_lock(pipe);
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index d5e1cb53d86a..ab955e4c3e5d 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1096,7 +1096,6 @@ static void fuse_send_readpages(struct fuse_io_args *ia, 
struct file *file,
 
        ap->args.out_pages = true;
        ap->args.page_zeroing = true;
-       ap->args.page_replace = true;
 
        /*
         * Don't overflow end offset.
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index 4b412a76225f..a90ce26ded31 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -326,7 +326,6 @@ struct fuse_copy_state {
        unsigned int len;
        unsigned int offset;
        bool write:1;
-       bool move_folios:1;
        bool is_uring:1;
        /* set when the payload is zero-copied. folios are filled in place */
        bool skip_folio_copy:1;
-- 
2.54.0


Reply via email to