->try_steal() lets the consumer of a pipe buffer take its page instead
of copying the data.  Two consumers use it:

  - fuse, when a server splices a read reply into /dev/fuse with
    SPLICE_F_MOVE: the page goes into the fuse file's page cache;
  - virtio_console, which passes the page to the host.

The page can come from the page cache of another file, from a write()
into the pipe, or from vmsplice(SPLICE_F_GIFT).  Stealing it takes page
cache folios out of their files, and turns pages into page cache folios
and back.  That gets in the way of reading the page cache without folio
references [1] and of telling folios apart with a PG_folio flag.  It
works only on pages that nothing else holds, for fuse only on clean,
unmapped, order-0 ones, and it has needed fixes again and again:

  32f98877c57b ("fuse: don't check refcount after stealing page")
  b89ecd60d38e ("fuse: ignore PG_workingset after stealing")
  712a951025c0 ("fuse: fix page stealing")
  59ba47b6be9c ("fuse: Check for large folio with SPLICE_F_MOVE")
  15c20483d915 ("fuse: validate folio state before pipe_buf_try_steal() in 
fuse_try_move_folio()")

Linus and David suggested removing it [2].  Both consumers already copy
when a steal fails; with this series they always do.  vmsplice() and
splice() still accept SPLICE_F_GIFT and SPLICE_F_MOVE, and fuse still
advertises FUSE_SPLICE_MOVE, so that servers which ask for it mount as
before.

Miklos, Amit: do you know of users of the fuse move, or of virtio_console
splice, that depend on the copy being avoided?

Without ->try_steal(), nosteal_pipe_buf_ops is the same as
default_pipe_buf_ops, and the last patch keeps only the latter.

Tested on x86_64 with a FUSE server that answers reads by splicing the
page cache pages of a backing file into /dev/fuse with SPLICE_F_MOVE.  On
v7.4-rc1 how much moves depends on folio sizes: with order-0 folios on
both sides, one read of a 16M fuse file took 3968 of the backing file's
4096 pages out of its page cache.  With the series nothing moves, and the
data read through fuse is the same, also when the server reads its
requests with splice().  A virtio-serial port fed by splice() from a pipe
and from a file, and vmsplice(SPLICE_F_GIFT), SPLICE_F_MOVE, tee() and
AF_UNIX socket splice checks also give the same data before and after.

[1] https://lore.kernel.org/all/[email protected]/
[2] 
https://lore.kernel.org/all/CAHk-=whhp=q8qr+wpom9+mxypq_oxmoeokmt7j0jzikfzus...@mail.gmail.com/

Kiryl Shutsemau (Meta) (4):
  virtio_console: copy pipe buffers in splice_write()
  fuse: don't move pipe buffers into the page cache
  splice: remove pipe buffer stealing
  splice: replace nosteal_pipe_buf_ops with default_pipe_buf_ops

 drivers/char/virtio_console.c |  39 ++++-----
 fs/fuse/args.h                |   1 -
 fs/fuse/dev.c                 | 146 +---------------------------------
 fs/fuse/file.c                |   1 -
 fs/fuse/fuse_dev_i.h          |   1 -
 fs/pipe.c                     |  43 ----------
 fs/splice.c                   | 104 ++----------------------
 include/linux/pipe_fs_i.h     |  38 ---------
 kernel/watch_queue.c          |   4 -
 mm/shmem.c                    |   7 --
 net/core/skbuff.c             |   2 +-
 11 files changed, 26 insertions(+), 360 deletions(-)


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.54.0


Reply via email to