* [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io
@ 2026-10-12 18:46 Stefan Metzmacher
2026-10-21 11:04 ` Christian Brauner
0 siblings, 1 reply; 8+ messages in thread
From: Stefan Metzmacher @ 2026-10-12 18:46 UTC (permalink / raw)
To: linux-fsdevel
Cc: metze, Alexander Viro, Christian Brauner, Jan Kara, io-uring,
linux-api
It's useful to control this per io request instead of using O_DIRECT
for the file descriptor.
E.g. the SMB2_READFLAG_READ_UNBUFFERED and SMB2_WRITEFLAG_WRITE_UNBUFFERED
are also available per io request and this allows Samba to map then
to RWF_DIRECT.
kiocb_set_rw_flags() requires FMODE_CAN_ODIRECT (same check open()
does for O_DIRECT) and returns -EOPNOTSUPP otherwise, matching the
convention already used by RWF_NOWAIT/RWF_ATOMIC/RWF_DONTCACHE just
above it in the same function for "this flag isn't supported on this file".
IOCB_DIRECT predates RWF_DIRECT used a kernel-internal value,
but now IOCB_DIRECT is aligned with RWF_DIRECT the same way
commit ce71bfea207b ("fs: align IOCB_* flags with RWF_* flags")
already aligned the other shared flags.
The man-page.git will get something like this for man/man2/readv.2:
RWF_DIRECT (since Linux XXX)
Provide a per-I/O equivalent of the O_DIRECT open(2) flag.
This allows a single preadv2() or pwritev2() call to bypass the
page cache without the file descriptor having been opened with
O_DIRECT. Unlike enabling O_DIRECT with the fcntl(2) F_SETFL
operation, this does not change the file status flags of
the open file description, so other I/O on the same file
descriptor, or on any file descriptor referring to the same
open file description (see dup(2)), is not affected.
If the underlying file or block device does not support
O_DIRECT, the call fails; see ERRORS. As with O_DIRECT open(2),
the memory buffer, the file offset, and the I/O length
generally need to be aligned to the logical block size of
the underlying storage.
man/man2/io_submit.2 will also get updated and liburing
in man/io_uring_prep_readv2.3 and man/io_uring_prep_writev2.3
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: linux-fsdevel@vger.kernel.org
Cc: io-uring@vger.kernel.org
Cc: linux-api@vger.kernel.org
Signed-off-by: Stefan Metzmacher <metze@samba.org>
---
include/linux/fs.h | 10 ++++++++--
include/uapi/linux/fs.h | 5 ++++-
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 83d41d9f1130..40d013a49378 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -344,10 +344,11 @@ struct readahead_control;
#define IOCB_ATOMIC (__force int) RWF_ATOMIC
#define IOCB_DONTCACHE (__force int) RWF_DONTCACHE
#define IOCB_NOSIGNAL (__force int) RWF_NOSIGNAL
+#define IOCB_DIRECT (__force int) RWF_DIRECT
/* non-RWF related bits - start at 16 */
+#define IOCB_NON_RWF_MASK (~0xffff)
#define IOCB_EVENTFD (1 << 16)
-#define IOCB_DIRECT (1 << 17)
#define IOCB_WRITE (1 << 18)
/* iocb->ki_waitq is valid */
#define IOCB_WAITQ (1 << 19)
@@ -3453,7 +3454,7 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags,
int kiocb_flags = 0;
/* make sure there's no overlap between RWF and private IOCB flags */
- BUILD_BUG_ON((__force int) RWF_SUPPORTED & IOCB_EVENTFD);
+ BUILD_BUG_ON((__force int) RWF_SUPPORTED & IOCB_NON_RWF_MASK);
if (!flags)
return 0;
@@ -3480,6 +3481,11 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags,
if (IS_DAX(ki->ki_filp->f_mapping->host))
return -EOPNOTSUPP;
}
+ if (flags & RWF_DIRECT) {
+ /* file system must support it, same as O_DIRECT */
+ if (!(ki->ki_filp->f_mode & FMODE_CAN_ODIRECT))
+ return -EOPNOTSUPP;
+ }
kiocb_flags |= (__force int) (flags & RWF_SUPPORTED);
if (flags & RWF_SYNC)
kiocb_flags |= IOCB_DSYNC;
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index a46c33692aa2..3aa5fd73d391 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -451,10 +451,13 @@ typedef int __bitwise __kernel_rwf_t;
/* prevent pipe and socket writes from raising SIGPIPE */
#define RWF_NOSIGNAL ((__force __kernel_rwf_t)0x00000100)
+/* per-IO O_DIRECT */
+#define RWF_DIRECT ((__force __kernel_rwf_t)0x00000200)
+
/* mask of flags supported by the kernel */
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\
RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\
- RWF_DONTCACHE | RWF_NOSIGNAL)
+ RWF_DONTCACHE | RWF_NOSIGNAL | RWF_DIRECT)
#define PROCFS_IOCTL_MAGIC 'f'
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-10-12 18:46 [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io Stefan Metzmacher @ 2026-10-21 11:04 ` Christian Brauner 2026-11-03 8:07 ` Christoph Hellwig 0 siblings, 1 reply; 8+ messages in thread From: Christian Brauner @ 2026-10-21 11:04 UTC (permalink / raw) To: Stefan Metzmacher, Christoph Hellwig Cc: linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api On Mon, Oct 12, 2026 at 08:46:42PM +0200, Stefan Metzmacher wrote: > It's useful to control this per io request instead of using O_DIRECT > for the file descriptor. @Christoph thoughts on this? > E.g. the SMB2_READFLAG_READ_UNBUFFERED and SMB2_WRITEFLAG_WRITE_UNBUFFERED > are also available per io request and this allows Samba to map then > to RWF_DIRECT. > > kiocb_set_rw_flags() requires FMODE_CAN_ODIRECT (same check open() > does for O_DIRECT) and returns -EOPNOTSUPP otherwise, matching the > convention already used by RWF_NOWAIT/RWF_ATOMIC/RWF_DONTCACHE just > above it in the same function for "this flag isn't supported on this file". > > IOCB_DIRECT predates RWF_DIRECT used a kernel-internal value, > but now IOCB_DIRECT is aligned with RWF_DIRECT the same way > commit ce71bfea207b ("fs: align IOCB_* flags with RWF_* flags") > already aligned the other shared flags. > > The man-page.git will get something like this for man/man2/readv.2: > > RWF_DIRECT (since Linux XXX) > Provide a per-I/O equivalent of the O_DIRECT open(2) flag. > This allows a single preadv2() or pwritev2() call to bypass the > page cache without the file descriptor having been opened with > O_DIRECT. Unlike enabling O_DIRECT with the fcntl(2) F_SETFL > operation, this does not change the file status flags of > the open file description, so other I/O on the same file > descriptor, or on any file descriptor referring to the same > open file description (see dup(2)), is not affected. > If the underlying file or block device does not support > O_DIRECT, the call fails; see ERRORS. As with O_DIRECT open(2), > the memory buffer, the file offset, and the I/O length > generally need to be aligned to the logical block size of > the underlying storage. > > man/man2/io_submit.2 will also get updated and liburing > in man/io_uring_prep_readv2.3 and man/io_uring_prep_writev2.3 > > Cc: Alexander Viro <viro@zeniv.linux.org.uk> > Cc: Christian Brauner <brauner@kernel.org> > Cc: Jan Kara <jack@suse.cz> > Cc: linux-fsdevel@vger.kernel.org > Cc: io-uring@vger.kernel.org > Cc: linux-api@vger.kernel.org > Signed-off-by: Stefan Metzmacher <metze@samba.org> > --- > include/linux/fs.h | 10 ++++++++-- > include/uapi/linux/fs.h | 5 ++++- > 2 files changed, 12 insertions(+), 3 deletions(-) > > diff --git a/include/linux/fs.h b/include/linux/fs.h > index 83d41d9f1130..40d013a49378 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -344,10 +344,11 @@ struct readahead_control; > #define IOCB_ATOMIC (__force int) RWF_ATOMIC > #define IOCB_DONTCACHE (__force int) RWF_DONTCACHE > #define IOCB_NOSIGNAL (__force int) RWF_NOSIGNAL > +#define IOCB_DIRECT (__force int) RWF_DIRECT > > /* non-RWF related bits - start at 16 */ > +#define IOCB_NON_RWF_MASK (~0xffff) > #define IOCB_EVENTFD (1 << 16) > -#define IOCB_DIRECT (1 << 17) > #define IOCB_WRITE (1 << 18) > /* iocb->ki_waitq is valid */ > #define IOCB_WAITQ (1 << 19) > @@ -3453,7 +3454,7 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags, > int kiocb_flags = 0; > > /* make sure there's no overlap between RWF and private IOCB flags */ > - BUILD_BUG_ON((__force int) RWF_SUPPORTED & IOCB_EVENTFD); > + BUILD_BUG_ON((__force int) RWF_SUPPORTED & IOCB_NON_RWF_MASK); > > if (!flags) > return 0; > @@ -3480,6 +3481,11 @@ static inline int kiocb_set_rw_flags(struct kiocb *ki, rwf_t flags, > if (IS_DAX(ki->ki_filp->f_mapping->host)) > return -EOPNOTSUPP; > } > + if (flags & RWF_DIRECT) { > + /* file system must support it, same as O_DIRECT */ > + if (!(ki->ki_filp->f_mode & FMODE_CAN_ODIRECT)) > + return -EOPNOTSUPP; > + } > kiocb_flags |= (__force int) (flags & RWF_SUPPORTED); > if (flags & RWF_SYNC) > kiocb_flags |= IOCB_DSYNC; > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h > index a46c33692aa2..3aa5fd73d391 100644 > --- a/include/uapi/linux/fs.h > +++ b/include/uapi/linux/fs.h > @@ -451,10 +451,13 @@ typedef int __bitwise __kernel_rwf_t; > /* prevent pipe and socket writes from raising SIGPIPE */ > #define RWF_NOSIGNAL ((__force __kernel_rwf_t)0x00000100) > > +/* per-IO O_DIRECT */ > +#define RWF_DIRECT ((__force __kernel_rwf_t)0x00000200) > + > /* mask of flags supported by the kernel */ > #define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\ > RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\ > - RWF_DONTCACHE | RWF_NOSIGNAL) > + RWF_DONTCACHE | RWF_NOSIGNAL | RWF_DIRECT) > > #define PROCFS_IOCTL_MAGIC 'f' > > -- > 2.43.0 > -- ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-10-21 11:04 ` Christian Brauner @ 2026-11-03 8:07 ` Christoph Hellwig 2026-11-03 13:06 ` Stefan Metzmacher ` (2 more replies) 0 siblings, 3 replies; 8+ messages in thread From: Christoph Hellwig @ 2026-11-03 8:07 UTC (permalink / raw) To: Christian Brauner Cc: Stefan Metzmacher, Christoph Hellwig, linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api On Wed, Oct 21, 2026 at 01:04:06PM +0200, Christian Brauner wrote: > On Mon, Oct 12, 2026 at 08:46:42PM +0200, Stefan Metzmacher wrote: > > It's useful to control this per io request instead of using O_DIRECT > > for the file descriptor. > > @Christoph thoughts on this? Puh. This goes right into a big discussion right now. We've traditionally said mixing buffered and direct I/O is madness, and that's certainly not untrue. On the other hand people find this useful - nfsd is now making heavy use of it, and a few year ago there was a paper on how the lustre backend really gained by doing that. So while some folks like Dave Chinner still get really angry at the idea of mixing the modes, I feel the ship has sailed and we need to make it work as good as possible. For some cases like mmap'ed I/O it will always introduce data corruption, but we'd at least make sure there's nothing exploitable. And the direct user exposed flag would open lots of these exploit vectors. There's also issues where file systems still check O_DIRECT for behavior and not IOCB_DIRECT, and this would increase the problems with that compared to the existing in-kernel only users. NFS for example has quite a few O_DIRECT checks, and some of them feel wrong for this. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-11-03 8:07 ` Christoph Hellwig @ 2026-11-03 13:06 ` Stefan Metzmacher 2026-11-03 13:51 ` Paulo Alcantara 2026-11-03 19:13 ` Andy Lutomirski 2 siblings, 0 replies; 8+ messages in thread From: Stefan Metzmacher @ 2026-11-03 13:06 UTC (permalink / raw) To: Christoph Hellwig, Christian Brauner Cc: linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api Am 03.11.26 um 09:07 schrieb Christoph Hellwig: > On Wed, Oct 21, 2026 at 01:04:06PM +0200, Christian Brauner wrote: >> On Mon, Oct 12, 2026 at 08:46:42PM +0200, Stefan Metzmacher wrote: >>> It's useful to control this per io request instead of using O_DIRECT >>> for the file descriptor. >> >> @Christoph thoughts on this? > > Puh. This goes right into a big discussion right now. We've > traditionally said mixing buffered and direct I/O is madness, and > that's certainly not untrue. On the other hand people find this > useful - nfsd is now making heavy use of it, and a few year ago > there was a paper on how the lustre backend really gained by doing > that. So while some folks like Dave Chinner still get really angry > at the idea of mixing the modes, I feel the ship has sailed and we > need to make it work as good as possible. For some cases like > mmap'ed I/O it will always introduce data corruption, but we'd at > least make sure there's nothing exploitable. And the direct user > exposed flag would open lots of these exploit vectors. Userspace can already mix the modes on the same file today, by opening it twice, once with and once without O_DIRECT, or even on a single struct file with fcntl(F_SETFL, O_DIRECT). The patch requires FMODE_CAN_ODIRECT in kiocb_set_rw_flags(), the same check open() and fcntl() do for O_DIRECT, so it doesn't allow direct I/O on files that couldn't do it before. The use case is Samba: SMB3 clients request unbuffered I/O per request (SMB2_READFLAG_READ_UNBUFFERED and SMB2_WRITEFLAG_WRITE_UNBUFFERED). Samba wants to use a strategy similar to NFSD_IO_DIRECT in nfsd: for reads the range is aligned down and up to the STATX_DIOALIGN values and read with RWF_DIRECT, for writes the aligned middle is written with RWF_DIRECT and the unaligned head and tail (if any) with RWF_DONTCACHE (or plain buffered I/O if the file system doesn't support it). Without RWF_DIRECT Samba would have to keep a second O_DIRECT file descriptor for every open, which only hides the mixing from the kernel. > There's also issues where file systems still check O_DIRECT for > behavior and not IOCB_DIRECT, and this would increase the problems > with that compared to the existing in-kernel only users. > NFS for example has quite a few O_DIRECT checks, and some of them > feel wrong for this. Right, that's the real difference: with two opens or F_SETFL the O_DIRECT in file->f_flags always matches IOCB_DIRECT, with RWF_DIRECT it doesn't, and FMODE_CAN_ODIRECT alone doesn't tell whether a file system copes with that. For some file systems FMODE_CAN_ODIRECT is still only derived from ->direct_IO = noop_direct_IO in do_dentry_open(), so it just means that O_DIRECT can be opened. cifs.ko is an example: with cache=loose cifs_file_write_iter() decides by O_DIRECT, so an RWF_DIRECT write would be written unbuffered by netfs, but without the cache invalidation and oplock reset cifs does for O_DIRECT. So what about making it opt-in as a start, like RWF_DONTCACHE with FOP_DONTCACHE? A new FOP_DIRECT_PER_IO would be set by file systems that base their I/O decisions on IOCB_DIRECT, all others return -EOPNOTSUPP for RWF_DIRECT. Once all file systems that set FMODE_CAN_ODIRECT also set FOP_DIRECT_PER_IO, the flag could be removed again and RWF_DIRECT would just depend on FMODE_CAN_ODIRECT. I looked at the local file systems Samba typically exports in v7.4-rc1: xfs, ext4, btrfs and f2fs (and also ext2, exfat, gfs2, nilfs2, zonefs, ntfs and ceph) decide per I/O based on iocb->ki_flags & IOCB_DIRECT, falling back to buffered I/O per request where direct I/O isn't possible (e.g. ext4 for encrypted, verity or inline data files, nilfs2 for writes) or failing it with -EOPNOTSUPP (f2fs for atomic write files, ntfs for compressed or encrypted files). O_DIRECT in file->f_flags is only used for checks at open time (or the start of an f2fs atomic write), so they could set FOP_DIRECT_PER_IO right away. Others like cifs, nfs, 9p, overlayfs and the other backing file users, ntfs3, erofs, ocfs2 and fuse have O_DIRECT dependencies in their open or I/O paths, they just wouldn't set FOP_DIRECT_PER_IO for now. Fixing them is independent of RWF_DIRECT. Should I send a v2 that adds FOP_DIRECT_PER_IO and sets it for the file systems listed above? metze ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-11-03 8:07 ` Christoph Hellwig 2026-11-03 13:06 ` Stefan Metzmacher @ 2026-11-03 13:51 ` Paulo Alcantara 2026-11-03 19:13 ` Andy Lutomirski 2 siblings, 0 replies; 8+ messages in thread From: Paulo Alcantara @ 2026-11-03 13:51 UTC (permalink / raw) To: Christoph Hellwig, Christian Brauner Cc: Stefan Metzmacher, Christoph Hellwig, linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api Christoph Hellwig <hch@lst.de> writes: > On Wed, Oct 21, 2026 at 01:04:06PM +0200, Christian Brauner wrote: >> On Mon, Oct 12, 2026 at 08:46:42PM +0200, Stefan Metzmacher wrote: >> > It's useful to control this per io request instead of using O_DIRECT >> > for the file descriptor. >> >> @Christoph thoughts on this? > > Puh. This goes right into a big discussion right now. We've > traditionally said mixing buffered and direct I/O is madness, and > that's certainly not untrue. On the other hand people find this > useful - nfsd is now making heavy use of it, and a few year ago > there was a paper on how the lustre backend really gained by doing > that. So while some folks like Dave Chinner still get really angry > at the idea of mixing the modes, I feel the ship has sailed and we > need to make it work as good as possible. For some cases like > mmap'ed I/O it will always introduce data corruption, but we'd at > least make sure there's nothing exploitable. And the direct user > exposed flag would open lots of these exploit vectors. Yeah. We also have xfstests that do mmap of O_DIRECT file descriptors, so mixing buffered and DIO to same file. Those particular tests fail against Windows Server due to unaligned IO. The CIFS client ends up using an handle opened for unbuffered IO in writeback, so the server will reject any unaligned IO to it. It works against Samba, however, which doesn't enforce any alignment. This could be fixed by using the per-io SMB flags that metze mentioned, but unfortunately the client still can't use it as some servers like Azure don't support them and will fail the requests rather than ignoring the unsupported flags. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-11-03 8:07 ` Christoph Hellwig 2026-11-03 13:06 ` Stefan Metzmacher 2026-11-03 13:51 ` Paulo Alcantara @ 2026-11-03 19:13 ` Andy Lutomirski 2026-11-04 7:30 ` direct I/O vs buffered I/O coherency, was: " Christoph Hellwig 2 siblings, 1 reply; 8+ messages in thread From: Andy Lutomirski @ 2026-11-03 19:13 UTC (permalink / raw) To: Christoph Hellwig Cc: Christian Brauner, Stefan Metzmacher, linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api On Tue, Nov 3, 2026 at 12:11 AM Christoph Hellwig <hch@lst.de> wrote: > > On Wed, Oct 21, 2026 at 01:04:06PM +0200, Christian Brauner wrote: > > On Mon, Oct 12, 2026 at 08:46:42PM +0200, Stefan Metzmacher wrote: > > > It's useful to control this per io request instead of using O_DIRECT > > > for the file descriptor. > > > > @Christoph thoughts on this? > > For some cases like > mmap'ed I/O it will always introduce data corruption, but we'd at > least make sure there's nothing exploitable. And the direct user > exposed flag would open lots of these exploit vectors. I feel like we've set the bar quite low here, and I wish the bar were higher. Why does mmap need become incoherent when someone does an O_DIRECT write? Brain dump follows. More generally, I've been contemplating page cache coherence for a few days (unrelated to this), and I have not convinced myself that there is any sort of principled concept of coherence that Linux implements, and the closest I can come to being confident that nothing is seriously broken is that I've only been able to find one seriously broken thing recently (now fixed), and, worse I don't even have a clean definition of what would constitute "seriously broken". (Which means that I can't, say, task an LLM with finding serious breaks or a moderately compelling proof that there aren't any.) Here's a bit of a taxonomy of page cache access types that I've come up with from poking around at the kernel source [0]: a) Plain buffered I/O (pread, pwrite, etc) -- I'm sure there is a lot of nuance here, but I'm generally referring to I/O, via pagecache, done in syscall or io_uring context on behalf of a user request. b) Access via MAP_SHARED mmap or via unbroken-cow MAP_PRIVATE read, whether this is actual ordinary user access or one of the weirder cases like KVM that hits it by cheating a bit and using mmu_notifier. c) O_DIRECT: not actually page cache, but it does *logically* touch the same data that might be in page cache. d) Access via pinned folio. We have reads and writes, and we have non-longterm and longterm pins. In my mind (and I'm sure I've missed at least as many subtleties as I've found), (a) is straightforward in the sense that the kernel can take appropriate locks. And the kernel takes locks that do *not* give strict coherence but give a weaker sort, I think -- see below. (b) can be fully coherent in the sense that the kernel can, and hopefully does, invalidate mappings and flush the TLB as needed and all access via mmap respects that (I hope -- I have not even tried to review the various mmu_notifier users, but I sincerely hope they all do it right). (d) is gnarly: we have the xarray lock and the folio lock and various pin bits, but we don't appear to have a lock that is safe to hold for any extended period of time that preserves a folio's role as the authoritative source of the contents of the part of the mapping it covers. In some sense, pinning a folio is like mmapping and faulting in that same folio, except that the kernel can zap the PTEs and invalidate the mapping from a different thread, but it cannot zap folio pins. (c) is supposed to be fast, and as far as I've found, the only technical (as opposed to performance) reason it can't be coherent is that there is no way for an O_DIRECT access to interact correctly with the existence of a pinned folio. But why can't an O_DIRECT access be coherent vs mmap? (I'm assuming we're talking about O_DIRECT writes, not reads.) ISTM the kernel has two ways to do it coherently: unmap the mapping (slow) or copy the data being written into page cache and then write it from page cache (differently slow). If anyone mmaps a file and O_DIRECT-writes it while they're touching the mapping at high frequency, I'm not sure they deserve excellent performance, and I'd rather give coherence than high performance incoherent behavior. Anyway, I have a sort of proposed definition for what coherence might mean -- call this "weak coherence": For each address in the file, there are various accesses. They happen in an order -- think of this like a CPU memory model, not a strictly linearizable order (if nothing else, mmaped accesses literally follow the CPU's memory model). But we broaden the memory model to pretend that it applies to the logical file, not to the physical memory. The implementation is weakly coherent if: - Accesses via mmap obey the CPU memory model. - Accesses via syscall or io_uring *including fixed buffers* obey the CPU memory model, except that the access may be broken up into smaller accesses that happen in arbitrary order, so long as they all happen between the submission and the completion of the request. This includes direct I/O. - Accesses via longterm folio pin follow the same rules as mmap. - Accesses via shortterm folio pin are like syscalls: they happen some time between acquisition and release of the pin but are not necessarily ordered at the time that they are made by the CPU. (This is the weak part.) I haven't come up with a reason that this would be impossible to implement nicely, except that longterm folio pins would need a much stricter locking scheme: anything that wants to detach a folio from an address_space would need to wait for the pin to be released or actively expedite the pin's release. Is there a hard issue with O_DIRECT that I haven't thought of? And someone would need to go through all the short-term pins in the tree and make sure they're all actually okay with the weak form of coherence. I think there's at least one example that I think is questionable: iov_iter_extract_user_pages, as reachable from FUSE and possibly elsewhere. Suppose there are two FUSE filesystems, A and B. A has a file a and B has a file b. A process mmaps b, faults it in, and does read(a_fd, b_ptr, 1), and the FUSE kernel code takes a short-term pin on b_ptr's folio. Then, before A's daemon responds, B's daemon requests invalidation of b (via fuse_notify_inval_inode), which calls invalidate_inode_pages2_range, which removes the folio from b's page cache. Then A's daemon responds to the read via splice from a pipe, causing the kernel to write to the short-term pinned folio, which goes off into the void. Is this a bug? If B's invalidation happened because some external actor logically wrote to *the entire area covered by the read*, then arguably it's not -- under my coherence model, the read syscall can be deemed to have occurred immediately before that write and the results simply got overwritten. But if B's invalidation happened for a different reason (say an external write to a non-overlapping range), then I think this behavior is incorrect. Similarly, if there is just one FUSE and B is an ordinary filesystem, then userspace can do read(a_fd, b_ptr, 1) and overlap it with an O_DIRECT write to b at, say, offset 512, size 512. If the FS accepts that, then the folio gets detached and the read's update goes into the void. That last example seems more likely to hit if RWF_DIRECT shows up and some library is like "hey, cool, now I can use our shiny atomic direct I/O features in what used to be a buffered I/O path". And maybe this isn't a *security* bug, but it is an observable case where a simple read() call appears to succeed but does not actually update the destination buffer. Ouch. Anyway, that's my brain dump. [0] Despite the fact that I got my start doing serious kernel work as a result of writing a memory mapped database of sorts, I've never actually stared at this part of the code before now... --Andy -- Andy Lutomirski AMA Capital Management, LLC ^ permalink raw reply [flat|nested] 8+ messages in thread
* direct I/O vs buffered I/O coherency, was: Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-11-03 19:13 ` Andy Lutomirski @ 2026-11-04 7:30 ` Christoph Hellwig 2026-11-04 17:48 ` Andy Lutomirski 0 siblings, 1 reply; 8+ messages in thread From: Christoph Hellwig @ 2026-11-04 7:30 UTC (permalink / raw) To: Andy Lutomirski Cc: Christoph Hellwig, Christian Brauner, Stefan Metzmacher, linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api, willy On Tue, Nov 03, 2026 at 11:13:32AM -0800, Andy Lutomirski wrote: > Here's a bit of a taxonomy of page cache access types that I've come > up with from poking around at the kernel source [0]: > > a) Plain buffered I/O (pread, pwrite, etc) -- I'm sure there is a lot > of nuance here, but I'm generally referring to I/O, via pagecache, > done in syscall or io_uring context on behalf of a user request. > > b) Access via MAP_SHARED mmap or via unbroken-cow MAP_PRIVATE read, > whether this is actual ordinary user access or one of the weirder > cases like KVM that hits it by cheating a bit and using mmu_notifier. This really also needs to be split into b1) Read-only access through MAP_SHARED mmap without PROT_WRITE or unbroken-cow MAP_PRIVATE read. and b2) Writable access through writable MAP_SHARED mmap. > > c) O_DIRECT: not actually page cache, but it does *logically* touch > the same data that might be in page cache. As in the file side of direct I/O to make this clear. > d) Access via pinned folio. We have reads and writes, and we have > non-longterm and longterm pins. ... and this includes the "remote" side of direct I/O if done to file backed VMAs. We also still have e) access through get_user_pages without pins. e) really needs to die ASAP, but various maintainers have dragged their feed and kept it. And of course there's still vmsplice which kinda hardcodes this into the syscall interface. > But why can't an O_DIRECT access be coherent vs mmap? (I'm assuming > we're talking about O_DIRECT writes, not reads.) ISTM the kernel has > two ways to do it coherently: unmap the mapping (slow) or copy the > data being written into page cache and then write it from page cache > (differently slow). If anyone mmaps a file and O_DIRECT-writes it > while they're touching the mapping at high frequency, I'm not sure > they deserve excellent performance, and I'd rather give coherence than > high performance incoherent behavior. Because reads invalidate the mapping they are just as relevant. But yes, both could fall back to some form of buffered I/O if the relevant ranges are mapped. And maybe that is the best thing to do. Note that this also means we need to prevent new folio in the page cache from being instantiated while direct I/O is pending. For XFS which always take i_rwsem on buffered reads that's reasonably easy, although we'd need additional i_dio_count checks (or move to non-owner release of i_rwsem for direct I/O). But other file systems don't do this, and they get significantly better performance for small cached buffered writes, so they probably do not want to change that. Leaving the rest in here for context. > > > Anyway, I have a sort of proposed definition for what coherence might > mean -- call this "weak coherence": > > For each address in the file, there are various accesses. They happen > in an order -- think of this like a CPU memory model, not a strictly > linearizable order (if nothing else, mmaped accesses literally follow > the CPU's memory model). But we broaden the memory model to pretend > that it applies to the logical file, not to the physical memory. The > implementation is weakly coherent if: > > - Accesses via mmap obey the CPU memory model. > > - Accesses via syscall or io_uring *including fixed buffers* obey the > CPU memory model, except that the access may be broken up into smaller > accesses that happen in arbitrary order, so long as they all happen > between the submission and the completion of the request. This > includes direct I/O. > > - Accesses via longterm folio pin follow the same rules as mmap. > > - Accesses via shortterm folio pin are like syscalls: they happen some > time between acquisition and release of the pin but are not > necessarily ordered at the time that they are made by the CPU. (This > is the weak part.) > > I haven't come up with a reason that this would be impossible to > implement nicely, except that longterm folio pins would need a much > stricter locking scheme: anything that wants to detach a folio from an > address_space would need to wait for the pin to be released or > actively expedite the pin's release. > > Is there a hard issue with O_DIRECT that I haven't thought of? > > And someone would need to go through all the short-term pins in the > tree and make sure they're all actually okay with the weak form of > coherence. > > I think there's at least one example that I think is questionable: > iov_iter_extract_user_pages, as reachable from FUSE and possibly > elsewhere. Suppose there are two FUSE filesystems, A and B. A has a > file a and B has a file b. A process mmaps b, faults it in, and does > read(a_fd, b_ptr, 1), and the FUSE kernel code takes a short-term pin > on b_ptr's folio. Then, before A's daemon responds, B's daemon > requests invalidation of b (via fuse_notify_inval_inode), which calls > invalidate_inode_pages2_range, which removes the folio from b's page > cache. Then A's daemon responds to the read via splice from a pipe, > causing the kernel to write to the short-term pinned folio, which goes > off into the void. > > Is this a bug? If B's invalidation happened because some external > actor logically wrote to *the entire area covered by the read*, then > arguably it's not -- under my coherence model, the read syscall can be > deemed to have occurred immediately before that write and the results > simply got overwritten. But if B's invalidation happened for a > different reason (say an external write to a non-overlapping range), > then I think this behavior is incorrect. > > Similarly, if there is just one FUSE and B is an ordinary filesystem, > then userspace can do read(a_fd, b_ptr, 1) and overlap it with an > O_DIRECT write to b at, say, offset 512, size 512. If the FS accepts > that, then the folio gets detached and the read's update goes into the > void. > > > That last example seems more likely to hit if RWF_DIRECT shows up and > some library is like "hey, cool, now I can use our shiny atomic direct > I/O features in what used to be a buffered I/O path". And maybe this > isn't a *security* bug, but it is an observable case where a simple > read() call appears to succeed but does not actually update the > destination buffer. Ouch. > > > Anyway, that's my brain dump. > > > [0] Despite the fact that I got my start doing serious kernel work as > a result of writing a memory mapped database of sorts, I've never > actually stared at this part of the code before now... > > > --Andy > > > -- > Andy Lutomirski > AMA Capital Management, LLC ---end quoted text--- ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: direct I/O vs buffered I/O coherency, was: Re: [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io 2026-11-04 7:30 ` direct I/O vs buffered I/O coherency, was: " Christoph Hellwig @ 2026-11-04 17:48 ` Andy Lutomirski 0 siblings, 0 replies; 8+ messages in thread From: Andy Lutomirski @ 2026-11-04 17:48 UTC (permalink / raw) To: Christoph Hellwig Cc: Christian Brauner, Stefan Metzmacher, linux-fsdevel, Alexander Viro, Jan Kara, io-uring, linux-api, willy > On Nov 3, 2026, at 11:31 PM, Christoph Hellwig <hch@lst.de> wrote: > > On Tue, Nov 03, 2026 at 11:13:32AM -0800, Andy Lutomirski wrote: >> Here's a bit of a taxonomy of page cache access types that I've come >> up with from poking around at the kernel source [0]: >> >> a) Plain buffered I/O (pread, pwrite, etc) -- I'm sure there is a lot >> of nuance here, but I'm generally referring to I/O, via pagecache, >> done in syscall or io_uring context on behalf of a user request. >> >> b) Access via MAP_SHARED mmap or via unbroken-cow MAP_PRIVATE read, >> whether this is actual ordinary user access or one of the weirder >> cases like KVM that hits it by cheating a bit and using mmu_notifier. > > This really also needs to be split into > > b1) Read-only access through MAP_SHARED mmap without PROT_WRITE > or unbroken-cow MAP_PRIVATE read. > > and > > > b2) Writable access through writable MAP_SHARED mmap. To confirm: do you mean literally just MAP_SHARED, PROT_READ|PROT_WRITE and then reading? Or are you specifically referring to reads from a shared mapping in the writable state, i.e. after mkwrite has happened? > >> >> c) O_DIRECT: not actually page cache, but it does *logically* touch >> the same data that might be in page cache. > > As in the file side of direct I/O to make this clear. > >> d) Access via pinned folio. We have reads and writes, and we have >> non-longterm and longterm pins. > > ... and this includes the "remote" side of direct I/O if done to file > backed VMAs. > > We also still have > > e) access through get_user_pages without pins. > > e) really needs to die ASAP, but various maintainers have dragged > their feed and kept it. And of course there's still vmsplice which > kinda hardcodes this into the syscall interface. > >> But why can't an O_DIRECT access be coherent vs mmap? (I'm assuming >> we're talking about O_DIRECT writes, not reads.) ISTM the kernel has >> two ways to do it coherently: unmap the mapping (slow) or copy the >> data being written into page cache and then write it from page cache >> (differently slow). If anyone mmaps a file and O_DIRECT-writes it >> while they're touching the mapping at high frequency, I'm not sure >> they deserve excellent performance, and I'd rather give coherence than >> high performance incoherent behavior. > > Because reads invalidate the mapping they are just as relevant. > But yes, both could fall back to some form of buffered I/O if the > relevant ranges are mapped. And maybe that is the best thing to do. > > Note that this also means we need to prevent new folio in the page cache > from being instantiated while direct I/O is pending. For XFS which > always take i_rwsem on buffered reads that's reasonably easy, although > we'd need additional i_dio_count checks (or move to non-owner release > of i_rwsem for direct I/O). > But other file systems don't do this, and they get significantly better > performance for small cached buffered writes, so they probably do not > want to change that. > > Leaving the rest in here for context. >> >> >> Anyway, I have a sort of proposed definition for what coherence might >> mean -- call this "weak coherence": >> >> For each address in the file, there are various accesses. They happen >> in an order -- think of this like a CPU memory model, not a strictly >> linearizable order (if nothing else, mmaped accesses literally follow >> the CPU's memory model). But we broaden the memory model to pretend >> that it applies to the logical file, not to the physical memory. The >> implementation is weakly coherent if: >> >> - Accesses via mmap obey the CPU memory model. >> >> - Accesses via syscall or io_uring *including fixed buffers* obey the >> CPU memory model, except that the access may be broken up into smaller >> accesses that happen in arbitrary order, so long as they all happen >> between the submission and the completion of the request. This >> includes direct I/O. >> >> - Accesses via longterm folio pin follow the same rules as mmap. >> >> - Accesses via shortterm folio pin are like syscalls: they happen some >> time between acquisition and release of the pin but are not >> necessarily ordered at the time that they are made by the CPU. (This >> is the weak part.) >> >> I haven't come up with a reason that this would be impossible to >> implement nicely, except that longterm folio pins would need a much >> stricter locking scheme: anything that wants to detach a folio from an >> address_space would need to wait for the pin to be released or >> actively expedite the pin's release. >> >> Is there a hard issue with O_DIRECT that I haven't thought of? >> >> And someone would need to go through all the short-term pins in the >> tree and make sure they're all actually okay with the weak form of >> coherence. I should add two other current glitches that I forgot to mention. Neither of these are new discoveries. - short term writable folio pins that overlap with writeback: these can modify page cache without calling .mkwrite. This has been known for a long time. An LLM did a quick audit for me and suggested that this can result in a bad state on NFS. It did not flag checksumming filesystems like btrfs, nor did I ask, but from old memory I can imagine unfortunate interactions. - hole punching completely ignores folio pins, both short term and long term. I have a delightful reproducer I posted on a different thread. It is less than straightforward to fix without adding a way to break an existing pin unless we’re willing to start refusing to punch a hole or perhaps to write zeros instead of actually punching the hole. https://lore.kernel.org/all/CALCETrVpEP=HxVqdSP_AgA=U7ignwAgjz5Vi9+Z9dZsX5DXeLw@mail.gmail.com/ >> >> I think there's at least one example that I think is questionable: >> iov_iter_extract_user_pages, as reachable from FUSE and possibly >> elsewhere. Suppose there are two FUSE filesystems, A and B. A has a >> file a and B has a file b. A process mmaps b, faults it in, and does >> read(a_fd, b_ptr, 1), and the FUSE kernel code takes a short-term pin >> on b_ptr's folio. Then, before A's daemon responds, B's daemon >> requests invalidation of b (via fuse_notify_inval_inode), which calls >> invalidate_inode_pages2_range, which removes the folio from b's page >> cache. Then A's daemon responds to the read via splice from a pipe, >> causing the kernel to write to the short-term pinned folio, which goes >> off into the void. >> >> Is this a bug? If B's invalidation happened because some external >> actor logically wrote to *the entire area covered by the read*, then >> arguably it's not -- under my coherence model, the read syscall can be >> deemed to have occurred immediately before that write and the results >> simply got overwritten. But if B's invalidation happened for a >> different reason (say an external write to a non-overlapping range), >> then I think this behavior is incorrect. >> >> Similarly, if there is just one FUSE and B is an ordinary filesystem, >> then userspace can do read(a_fd, b_ptr, 1) and overlap it with an >> O_DIRECT write to b at, say, offset 512, size 512. If the FS accepts >> that, then the folio gets detached and the read's update goes into the >> void. >> >> >> That last example seems more likely to hit if RWF_DIRECT shows up and >> some library is like "hey, cool, now I can use our shiny atomic direct >> I/O features in what used to be a buffered I/O path". And maybe this >> isn't a *security* bug, but it is an observable case where a simple >> read() call appears to succeed but does not actually update the >> destination buffer. Ouch. >> >> >> Anyway, that's my brain dump. >> >> >> [0] Despite the fact that I got my start doing serious kernel work as >> a result of writing a memory mapped database of sorts, I've never >> actually stared at this part of the code before now... >> >> >> --Andy >> >> ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-11-04 17:48 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-12 18:46 [PATCH] fs: add RWF_DIRECT for preadv2/pwritev2/io_uring to set IOCB_DIRECT per io Stefan Metzmacher 2026-10-21 11:04 ` Christian Brauner 2026-11-03 8:07 ` Christoph Hellwig 2026-11-03 13:06 ` Stefan Metzmacher 2026-11-03 13:51 ` Paulo Alcantara 2026-11-03 19:13 ` Andy Lutomirski 2026-11-04 7:30 ` direct I/O vs buffered I/O coherency, was: " Christoph Hellwig 2026-11-04 17:48 ` Andy Lutomirski
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox