* Re: [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring [not found] ` <2026090913-landline-encroach-c544@gregkh> @ 2026-09-09 10:42 ` Li Wang 2026-09-09 13:35 ` Greg Kroah-Hartman 0 siblings, 1 reply; 13+ messages in thread From: Li Wang @ 2026-09-09 10:42 UTC (permalink / raw) To: Greg Kroah-Hartman Cc: Arnd Bergmann, Sumit Semwal, Christian König, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao Hi Greg, Thanks for the review! > > That's not really needed in a changelog text, it could be in the 0/X > patch :) > Sorry for the clutter. I will move most of them into the 0/X patch in v2. > Anyway, you didn't cc: the io_uring list, why? > `scripts/get_maintainer.pl` didn't output the io_uring mailing list, likely because this patch doesn't directly touch the io_uring codebase itself. It only enables remapping GPU memory buffers to CPU virtual addresses, which can then be consumed via standard io_uring APIs. I've added io-uring@vger.kernel.org to CC for this reply and will keep it in v2. > > Nor why "fgds" is the name, that's going to be hard to remember, does it > stand for something? > "FGDS" stands for Fast GPUDirect Storage. GPUDirect Storage (GDS) is NVIDIA's technology enabling direct I/O between GPU memory and files on NVMe, widely used in LLM workloads to bypass CPU overhead. If "FGDS" feels unintuitive, please let us know if you have a better alternative. We will address the rest of the code comments in the v2 patch series soon. Thanks, Li Wang ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring 2026-09-09 10:42 ` [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring Li Wang @ 2026-09-09 13:35 ` Greg Kroah-Hartman 2026-09-09 13:45 ` Christian König 0 siblings, 1 reply; 13+ messages in thread From: Greg Kroah-Hartman @ 2026-09-09 13:35 UTC (permalink / raw) To: Li Wang Cc: Arnd Bergmann, Sumit Semwal, Christian König, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On Wed, Sep 09, 2026 at 06:42:03PM +0800, Li Wang wrote: > Hi Greg, > Thanks for the review! > > > > > That's not really needed in a changelog text, it could be in the 0/X > > patch :) > > > Sorry for the clutter. I will move most of them into the 0/X patch in v2. > > > Anyway, you didn't cc: the io_uring list, why? > > > `scripts/get_maintainer.pl` didn't output the io_uring mailing list, likely > because this patch doesn't directly touch the io_uring codebase itself. It only > enables remapping GPU memory buffers to CPU virtual addresses, which can then be > consumed via standard io_uring APIs. > > I've added io-uring@vger.kernel.org to CC for this reply and will keep it in v2. Great, as you are using that as the api, there might be some parts that will need to be reviewed by them. > > Nor why "fgds" is the name, that's going to be hard to remember, does it > > stand for something? > > > "FGDS" stands for Fast GPUDirect Storage. GPUDirect Storage (GDS) is NVIDIA's > technology enabling direct I/O between GPU memory and files on NVMe, > widely used in LLM workloads to bypass CPU overhead. That's nvidia's specific solution, but this works on other devices, right? Or just for that one platform? And you are using this as a "bypass" for the normal accel subsystem, shouldn't this be part of that subsystem instead of a custom user/kernel api like you are creating here? thanks, greg k-h ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring 2026-09-09 13:35 ` Greg Kroah-Hartman @ 2026-09-09 13:45 ` Christian König 2026-09-10 3:57 ` Li Wang 0 siblings, 1 reply; 13+ messages in thread From: Christian König @ 2026-09-09 13:45 UTC (permalink / raw) To: Greg Kroah-Hartman, Li Wang Cc: Arnd Bergmann, Sumit Semwal, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On 9/9/26 15:35, Greg Kroah-Hartman wrote: > On Wed, Sep 09, 2026 at 06:42:03PM +0800, Li Wang wrote: >> Hi Greg, >> Thanks for the review! >> >>> >>> That's not really needed in a changelog text, it could be in the 0/X >>> patch :) >>> >> Sorry for the clutter. I will move most of them into the 0/X patch in v2. >> >>> Anyway, you didn't cc: the io_uring list, why? >>> >> `scripts/get_maintainer.pl` didn't output the io_uring mailing list, likely >> because this patch doesn't directly touch the io_uring codebase itself. It only >> enables remapping GPU memory buffers to CPU virtual addresses, which can then be >> consumed via standard io_uring APIs. >> >> I've added io-uring@vger.kernel.org to CC for this reply and will keep it in v2. > > Great, as you are using that as the api, there might be some parts that > will need to be reviewed by them. > >>> Nor why "fgds" is the name, that's going to be hard to remember, does it >>> stand for something? >>> >> "FGDS" stands for Fast GPUDirect Storage. GPUDirect Storage (GDS) is NVIDIA's >> technology enabling direct I/O between GPU memory and files on NVMe, >> widely used in LLM workloads to bypass CPU overhead. > > That's nvidia's specific solution, but this works on other devices, > right? Or just for that one platform? That was nvidia's specific and very hacky out of tree solution which as far as I know is pretty much abandoned everywhere. AMD came up with something similar, but all those approaches are so fundamentally broken that we didn't even considered upstreaming it. > And you are using this as a "bypass" for the normal accel subsystem, > shouldn't this be part of that subsystem instead of a custom user/kernel > api like you are creating here? As far as I know there is a patch set under review and even already partially merged which enables exactly that functionality as general feature for DMA-buf which is vendor independent and should at least in theory work with all drivers. I'm really surprised that somebody is still working on the vendor specific stuff. Regards, Christian. > > thanks, > > greg k-h ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring 2026-09-09 13:45 ` Christian König @ 2026-09-10 3:57 ` Li Wang 2026-09-10 8:35 ` Christian König 2026-09-12 12:45 ` Pavel Begunkov 0 siblings, 2 replies; 13+ messages in thread From: Li Wang @ 2026-09-10 3:57 UTC (permalink / raw) To: Christian König, Greg Kroah-Hartman Cc: Arnd Bergmann, Sumit Semwal, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao Hi Christian, >>>> Nor why "fgds" is the name, that's going to be hard to remember, does it >>>> stand for something? >>>> >>> "FGDS" stands for Fast GPUDirect Storage. GPUDirect Storage (GDS) is NVIDIA's >>> technology enabling direct I/O between GPU memory and files on NVMe, >>> widely used in LLM workloads to bypass CPU overhead. >> >> That's nvidia's specific solution, but this works on other devices, >> right? Or just for that one platform? > > That was nvidia's specific and very hacky out of tree solution Exactly, I completely agree with your viewpoint. In fact, we previously conducted a deep analysis of NVIDIA's GDS implementation code, which was also one of the motivations for us to develop fgds. Please allow me to introduce NVIDIA's GDS implementation briefly: NVIDIA introduced two kernel modules: one module called nvidia-fs, and the other module is a customized NVMe driver replacing the Linux kernel's default NVMe driver. nvidia-fs creates a device file for every NVIDIA GPU on the machine: /dev/nvidia-fs<gpu-id>. Applications call cuFileBufRegister to register the GPU memory buffer to perform I/O. cuFileBufRegister executes the following operations: calls nvidia-fs via ioctl. The implementation of this ioctl allocates a corresponding phony buffer of the same size in the host memory, and establishes a mapping between the CPU memory phony buffer and the GPU memory buffer. Then, the application calls cuFileRead/cuFileWrite to perform file I/O operations. Its implementation calls ioctl on nvidia-fs with NVFS_IOCTL_READ/NVFS_IOCTL_WRITE as parameters. Inside the ioctl implementation, it calls the common kernel interface filp->f_op->read_iter/write_iter() on the file on NVME. These interfaces can only take the CPU memory phony buffer address as a input, constructing read/write requests sent through the block layer to the customized NVMe driver. The customized NVMe driver intercepts the I/O operations, calls the nvidia-fs interface to query, replaces the phony buffer address with the GPU memory dma address, and performs DMA transfer between GPU memory and NVMe. As we can see, this implementation is indeed very hacky and non-elegant. Furthermore, as evaluated in this paper published in SC'25 [1], the phony buffer brings a considerable performance overhead. > which as far as I know is pretty much abandoned everywhere. However, despite the overhead of phony buffers, GDS performance is still significantly higher than transferring through CPU host memory (as shown in our performance benchmark tests [2]). Therefore, GDS is actually still widely used in the LLM ecosystem. For instance, model loading plugins used in inference engines like vLLM and SGLang—such as fastsafetensors and InstantTensor— both support acceleration via GDS [3,4], with fastsafetensors enabling GDS by default for model loading. Furthermore, LMCache, a plugin used for KV cache offloading in vLLM and SGLang, also supports GDS acceleration [5]. PyTorch itself also provides file access APIs based on GDS [6]. > > AMD came up with something similar, but all those approaches are so fundamentally broken that we didn't even considered upstreaming it. >> And you are using this as a "bypass" for the normal accel subsystem, >> shouldn't this be part of that subsystem instead of a custom user/kernel >> api like you are creating here? > > As far as I know there is a patch set under review and even already partially merged which enables exactly that functionality as general feature for DMA-buf which is vendor independent and should at least in theory work with all drivers. > > I'm really surprised that somebody is still working on the vendor specific stuff. As you pointed out, every vendor has been inventing their own way and interfaces to support GDS, introducing custom kernel modules and proprietary UAPI interfaces, with varying performance that leaves developers heavily frustrated. Apologies for not making this clear enough in our commit messages, which understandably caused some confusion. We merely borrowed the name "GDS" to describe the functional purpose of fgds. In fact, we believe fgds offers four key advantages: (1) GPU platform independence; (2) POSIX/io_uring interface compatibility; (3) Higher performance than GDS; (4) Minimal kernel footprint and UAPI footprint Regarding (1), (2), and (3), please allow me to briefly explain the design mechanism of fgds: fgds turns a GPU memory buffer into a POSIX/io_uring-compatible user-space virtual address via three main steps: Step 1: Utilizing ZONE_DEVICE support, we remap the GPU memory exposed via PCIe BAR into struct pages using devm_memremap_pages(); Step 2: Utilizing dma-buf support, the GPU memory buffer is exported as a dma-buf file descriptor (fd). Using this fd as a bridge, we look up the corresponding DMA addresses for the GPU memory buffer inside the kernel; Step 3: Through mmap, we insert the struct pages corresponding to the GPU memory buffer into the userspace VMA, mapping their physical/DMA addresses directly. The virtual address returned by mmap can then be directly passed into standard POSIX or io_uring interfaces. As you can see, since almost all major GPU vendors support exporting GPU memory buffers via dma-buf, all remaining technical dependencies of fgds rely on standard Linux kernel infrastructure. Therefore, fgds is completely vendor-agnostic and natively compatible with POSIX/io_uring without introducing any proprietary vendor interfaces, which greatly simplifies development, deployment, operations and unifies standard usage. Furthermore, because this technique completely eliminates the phony buffer, its performance is significantly better than NVIDIA's GDS (as shown in our benchmarks [2]). In addition, since fgds uses only the most fundamental dma-buf mechanisms, it relies on baseline dma-buf features that have been supported in the upstream kernel for a long time, rather than any new dma-buf features currently under active development. In fact, before we recently ported fgds to the latest kernel tree, it was developed and ran on our internal 6.6 kernel. It has been running stably in our production clusters for over half a year across various hardware platforms (including NVIDIA, AMD, and several other vendors) without requiring a single line of GPU-platform-specific fgds code modification. Regarding (4): We believe that implementing GDS-like functionality inherently requires kernel assistance to map the GPU memory buffer to a valid userspace virtual address. This inevitably requires userspace-kernel interaction. To the best of our knowledge, the mainline kernel currently lacks a dedicated, unified path for this specific interaction, which is why various vendors ended up writing their own out-of-tree interfaces. In contrast, fgds introduces only one single new ioctl parameter (REG_BUFFER, excluding UNREG_BUFFER), and confines its scope strictly to a standalone device driver. We believe this achieves a minimal kernel footprint and minimal UAPI addition. [1] https://dl.acm.org/doi/10.1145/3712285.3759862 [2] https://github.com/Storage-and-OS-for-AI/fgds [3] https://github.com/foundation-model-stack/fastsafetensors/blob/main/docs/configuration.md [4] https://github.com/scitix/InstantTensor/blob/main/csrc/loader_io_cufile.cpp [5] https://github.com/LMCache/LMCache/blob/dev/lmcache/v1/storage_backend/gds_backend.py [6] https://docs.pytorch.org/docs/2.14/generated/torch.cuda.gds.GdsFile.html Thanks, Li Wang> Regards, > Christian. > >> >> thanks, >> >> greg k-h ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring 2026-09-10 3:57 ` Li Wang @ 2026-09-10 8:35 ` Christian König 2026-09-12 12:45 ` Pavel Begunkov 1 sibling, 0 replies; 13+ messages in thread From: Christian König @ 2026-09-10 8:35 UTC (permalink / raw) To: Li Wang, Greg Kroah-Hartman Cc: Arnd Bergmann, Sumit Semwal, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On 9/10/26 05:57, Li Wang wrote: ... >> AMD came up with something similar, but all those approaches are so fundamentally broken that we didn't even considered upstreaming it. >>> And you are using this as a "bypass" for the normal accel subsystem, >>> shouldn't this be part of that subsystem instead of a custom user/kernel >>> api like you are creating here? >> >> As far as I know there is a patch set under review and even already partially merged which enables exactly that functionality as general feature for DMA-buf which is vendor independent and should at least in theory work with all drivers. >> >> I'm really surprised that somebody is still working on the vendor specific stuff. > As you pointed out, every vendor has been inventing their own way and interfaces to support GDS, > introducing custom kernel modules and proprietary UAPI interfaces, with varying performance that > leaves developers heavily frustrated. Apologies for not making this clear enough in our commit > messages, which understandably caused some confusion. We merely borrowed the name "GDS" to describe > the functional purpose of fgds. > > In fact, we believe fgds offers four key advantages: > (1) GPU platform independence; > (2) POSIX/io_uring interface compatibility; > (3) Higher performance than GDS; > (4) Minimal kernel footprint and UAPI footprint > > Regarding (1), (2), and (3), please allow me to briefly explain the design mechanism of fgds: > fgds turns a GPU memory buffer into a POSIX/io_uring-compatible user-space virtual address via > three main steps: > > Step 1: Utilizing ZONE_DEVICE support, we remap the GPU memory exposed via PCIe BAR into struct pages > using devm_memremap_pages(); > > Step 2: Utilizing dma-buf support, the GPU memory buffer is exported as a dma-buf file descriptor (fd). > Using this fd as a bridge, we look up the corresponding DMA addresses for the GPU memory buffer inside > the kernel; > > Step 3: Through mmap, we insert the struct pages corresponding to the GPU memory buffer into the userspace > VMA, mapping their physical/DMA addresses directly. The virtual address returned by mmap can then be directly > passed into standard POSIX or io_uring interfaces. Well long story short what you do here is completely broken. Approaches like those have been suggested before and we added both documentation as well as code to prevent such hacks from working. Please see Pavel Begunkov patch set on the LKML which adds DMA-buf support to io_uring for how to do it correctly. Just google for "Add dmabuf read/write via io_uring". Regards, Christian. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring 2026-09-10 3:57 ` Li Wang 2026-09-10 8:35 ` Christian König @ 2026-09-12 12:45 ` Pavel Begunkov 1 sibling, 0 replies; 13+ messages in thread From: Pavel Begunkov @ 2026-09-12 12:45 UTC (permalink / raw) To: Li Wang, Christian König, Greg Kroah-Hartman Cc: Arnd Bergmann, Sumit Semwal, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On 9/10/26 04:57, Li Wang wrote: > Hi Christian, > >>>>> Nor why "fgds" is the name, that's going to be hard to remember, does it >>>>> stand for something? >>>>> >>>> "FGDS" stands for Fast GPUDirect Storage. GPUDirect Storage (GDS) is NVIDIA's >>>> technology enabling direct I/O between GPU memory and files on NVMe, >>>> widely used in LLM workloads to bypass CPU overhead. >>> >>> That's nvidia's specific solution, but this works on other devices, >>> right? Or just for that one platform? >> >> That was nvidia's specific and very hacky out of tree solution > Exactly, I completely agree with your viewpoint. In fact, we previously conducted > a deep analysis of NVIDIA's GDS implementation code, which was also one of the > motivations for us to develop fgds. > > Please allow me to introduce NVIDIA's GDS implementation briefly: > NVIDIA introduced two kernel modules: one module called nvidia-fs, and the other module > is a customized NVMe driver replacing the Linux kernel's default NVMe driver. > nvidia-fs creates a device file for every NVIDIA GPU on the machine: /dev/nvidia-fs<gpu-id>. I'm not an nvidia expert, but IIRC they switched to P2PDMA, which is supported by the upstream nvme driver. -- Pavel Begunkov ^ permalink raw reply [flat|nested] 13+ messages in thread
* [RFC PATCH v2 0/1] misc: fgds: introduce GPU-File direct I/O via dma-buf [not found] <20260908131545.105987-1-liwang@kylinos.cn> [not found] ` <2026090913-landline-encroach-c544@gregkh> @ 2026-10-27 9:08 ` Li Wang 2026-10-27 9:08 ` [RFC PATCH v2 1/1] " Li Wang 2026-10-27 13:54 ` [RFC PATCH v2 0/1] " Pavel Begunkov 1 sibling, 2 replies; 13+ messages in thread From: Li Wang @ 2026-10-27 9:08 UTC (permalink / raw) To: Arnd Bergmann, Greg Kroah-Hartman Cc: Sumit Semwal, Christian König, Pavel Begunkov, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Li Wang This patch introduces a driver named fgds that allows moving data between a GPU dma-buf and a regular file on NVME without staging it through the page cache. This is particularly useful for LLM workloads, such as model loading, KV cache offloading, and checkpointing. The fgds device corresponds one-to-one with the PCIe GPU on the machine. The usage is straightforward: an application first calls ioctl on the corresponding fgds device with REGISTER_BUF, passing the target GPU memory buffer address (represented as a dma-buf fd). Subsequently, the application can perform direct data transfer between a regular file on NVMe and GPU memory by invoking ioctl on the fgds device with READ/WRITE, passing the file descriptor and the handle returned by REGISTER_BUF. This solution allows both applications and the fgds device to work seamlessly with GPUs from different vendors without any modifications. Furthermore, applications no longer need to call vendor-specific proprietary APIs (such as NVIDIA's cuFile API) or install vendor-specific kernel modules (such as NVIDIA's nvidia-fs.ko) for different GPU vendors. We have tested fgds on NVDIA and AMD GPU cards, and theoretically, it also works on GPUs from other vendors that support exporting GPU memory via dma-buf. Some performance test results can be found in [1]. This implementation offers several advantages: - Simplicity and Efficiency: It provides a lightweight and intuitive interface. The single ioctl call mimics the parameter structure of pread/pwrite, and its return value semantics are fully compatible with pread/pwrite - Isolation and Security: The interface is independent and has a minimal impact scope. This isolation facilitates fine-grained permission control on the device node, enhancing security management Changes since v1: - Refactor implementation per Christian's feedback, on top of Pavel's DMA-buf support for io_uring [2] - Add validation checks in the XFS I/O submission path to ensure that bios with the REQ_DMABUF flag are only processed via direct I/O, and prevents the kernel from attempting to fall back to buffered I/O, which is incompatible with DMA-buf backed GPU memory regions. While currently supports only XFS, the underlying mechanism is filesystem-agnostic and can be adapted for other filesystems with similar minor changes - Address the comments from Greg and the Sashiko AI [1] https://github.com/Storage-and-OS-for-AI/fgds [2] https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git/ tag/?h=for-7.4/io_uring-dmabuf-20261023 Mengmeng Zhao (1): misc: fgds: introduce GPU-File direct I/O via dma-buf .../userspace-api/ioctl/ioctl-number.rst | 2 + drivers/dma-buf/dma-buf-io.c | 3 + drivers/misc/Kconfig | 16 + drivers/misc/Makefile | 1 + drivers/misc/fgds.c | 506 ++++++++++++++++++ fs/iomap/buffered-io.c | 11 + fs/iomap/direct-io.c | 23 +- fs/xfs/xfs_file.c | 4 + include/linux/iov_iter.h | 4 + include/uapi/linux/fgds.h | 124 +++++ lib/iov_iter.c | 1 + mm/filemap.c | 22 +- 12 files changed, 713 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/fgds.c create mode 100644 include/uapi/linux/fgds.h -- 2.34.1 ^ permalink raw reply [flat|nested] 13+ messages in thread
* [RFC PATCH v2 1/1] misc: fgds: introduce GPU-File direct I/O via dma-buf 2026-10-27 9:08 ` [RFC PATCH v2 0/1] misc: fgds: introduce GPU-File direct I/O via dma-buf Li Wang @ 2026-10-27 9:08 ` Li Wang 2026-10-27 13:03 ` Jens Axboe 2026-10-27 13:12 ` Christian König 2026-10-27 13:54 ` [RFC PATCH v2 0/1] " Pavel Begunkov 1 sibling, 2 replies; 13+ messages in thread From: Li Wang @ 2026-10-27 9:08 UTC (permalink / raw) To: Arnd Bergmann, Greg Kroah-Hartman Cc: Sumit Semwal, Christian König, Pavel Begunkov, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao, Li Wang From: Mengmeng Zhao <zhaomengmeng@kylinos.cn> This patch introduces a driver that allows moving data between a GPU dma-buf and a regular file on NVME without staging it through the page cache. Userspace registers the GPU dma-buf with the device, then issues read and write requests via the device's ioctls. Each transfer is direct I/O: an ITER_DMABUF_MAP iterator is passed to the file's read_iter or write_iter, and the filesystem's iomap path submits REQ_DMABUF bios that already carry the dma addresses. Signed-off-by: Mengmeng Zhao <zhaomengmeng@kylinos.cn> Signed-off-by: Li Wang <liwang@kylinos.cn> --- .../userspace-api/ioctl/ioctl-number.rst | 2 + drivers/dma-buf/dma-buf-io.c | 3 + drivers/misc/Kconfig | 16 + drivers/misc/Makefile | 1 + drivers/misc/fgds.c | 506 ++++++++++++++++++ fs/iomap/buffered-io.c | 11 + fs/iomap/direct-io.c | 23 +- fs/xfs/xfs_file.c | 4 + include/linux/iov_iter.h | 4 + include/uapi/linux/fgds.h | 124 +++++ lib/iov_iter.c | 1 + mm/filemap.c | 22 +- 12 files changed, 713 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/fgds.c create mode 100644 include/uapi/linux/fgds.h diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 2fc53093752d..5f93a252e773 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -306,6 +306,8 @@ Code Seq# Include File Comments 'v' 20-27 arch/powerpc/include/uapi/asm/vas-api.h VAS API 'v' C0-FF linux/meye.h conflict! 'w' all CERN SCI driver +'x' 00-0F linux/fgds.h Fast GPU Direct Storage + <mailto:liwang@kylinos.cn> 'y' 00-1F packet based user level communications <mailto:zapman@interlan.net> 'z' 00-3F CAN bus card conflict! diff --git a/drivers/dma-buf/dma-buf-io.c b/drivers/dma-buf/dma-buf-io.c index 8440151de860..2ee4fc683c74 100644 --- a/drivers/dma-buf/dma-buf-io.c +++ b/drivers/dma-buf/dma-buf-io.c @@ -135,6 +135,7 @@ struct dma_buf_io_map *dma_buf_io_create_map(struct dma_buf_io_ctx *ctx) dma_resv_unlock(ctx->dmabuf->resv); return map; } +EXPORT_SYMBOL_NS_GPL(dma_buf_io_create_map, "DMA_BUF"); static void dma_buf_io_ctx_shutdown(struct dma_buf_io_ctx *ctx) { @@ -190,6 +191,7 @@ int dma_buf_io_ctx_create(struct file *file, *out_ctx = ctx; return 0; } +EXPORT_SYMBOL_NS_GPL(dma_buf_io_ctx_create, "DMA_BUF"); /** * dma_buf_io_ctx_release - Destroy the I/O context @@ -211,6 +213,7 @@ void dma_buf_io_ctx_release(struct dma_buf_io_ctx *ctx) dma_buf_put(ctx->dmabuf); kfree(ctx); } +EXPORT_SYMBOL_NS_GPL(dma_buf_io_ctx_release, "DMA_BUF"); /** * dma_buf_io_invalidate_mappings - Invalidate the current map diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7364931dad3a..b85fd64d27ad 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -568,6 +568,22 @@ config MCHP_LAN966X_PCI - lan966x-miim (MDIO_MSCC_MIIM) - lan966x-switch (LAN966X_SWITCH) +config FGDS + tristate "Fast GPU Direct Storage support" + depends on DMA_SHARED_BUFFER && BLOCK + default n + help + This driver exposes a misc device that moves data between a GPU + dma-buf and a regular file with synchronous read/write ioctls, + submitting the transfer as direct I/O (REQ_DMABUF bios) through + the file system's iomap DIO path without a page cache bounce + buffer. + + To compile this driver as a module, choose M here: the module + will be called fgds. + + If unsure, say N. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e8d8d5d88c0d..cd98ef3d008e 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -70,4 +70,5 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-$(CONFIG_FGDS) += fgds.o obj-$(CONFIG_INTEL_SSEI) += issei/ diff --git a/drivers/misc/fgds.c b/drivers/misc/fgds.c new file mode 100644 index 000000000000..3929a6514d23 --- /dev/null +++ b/drivers/misc/fgds.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Fast GPU Direct Storage via dma-buf. + * + * Copyright (C) 2026 KylinSoft. Co., Ltd. All rights reserved. + * + * Synchronous read/write ioctls that move data between a GPU dma-buf and + * a regular file. + */ + +#include <linux/blkdev.h> +#include <linux/dma-buf.h> +#include <linux/dma-buf-io.h> +#include <linux/device.h> +#include <linux/fcntl.h> +#include <linux/fileattr.h> +#include <linux/fs.h> +#include <linux/fsnotify.h> +#include <linux/magic.h> +#include <linux/math.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/pagemap.h> +#include <linux/sched/xacct.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/uio.h> +#include <linux/xarray.h> + +#include <linux/fgds.h> + +static struct miscdevice fgds_misc; + +/* One registered buffer: a dma-buf bound to a block device. */ +struct fgds_reg { + struct kref kref; + struct dma_buf_io_ctx *ctx; + struct file *bdev_file; + struct block_device *bdev; + u64 size; +}; + +/* State of one /dev/fgds open file description; handles are private to it. */ +struct fgds_state { + struct xarray regs; + /* protects @regs and, with the reg kref, the lifetime of entries */ + struct mutex lock; +}; + +static void fgds_reg_release(struct kref *kref) +{ + struct fgds_reg *reg = container_of(kref, struct fgds_reg, kref); + + dma_buf_io_ctx_release(reg->ctx); + fput(reg->bdev_file); + kfree(reg); +} + +static void fgds_reg_put(struct fgds_reg *reg) +{ + kref_put(®->kref, fgds_reg_release); +} + +static struct fgds_state *fgds_state(struct file *file) +{ + return file->private_data; +} + +/* + * File and file-system level gate. + */ +static int fgds_gate(struct file *ffile) +{ + struct inode *inode = file_inode(ffile); + struct super_block *sb = inode->i_sb; + struct file_kattr fa; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + if (sb->s_magic != XFS_SUPER_MAGIC) + return -EOPNOTSUPP; + if (IS_DAX(inode)) + return -EOPNOTSUPP; + if (mapping_stable_writes(inode->i_mapping)) + return -EOPNOTSUPP; + if (!sb->s_bdev) + return -EOPNOTSUPP; + if (bdev_is_zoned(sb->s_bdev)) + return -EOPNOTSUPP; + if (!sb->s_bdev->bd_disk->fops->init_dma_buf_io_ctx) + return -EOPNOTSUPP; + + /* + * Realtime inodes live on a separate device whose DMA setup is not + * covered here. + */ + if (vfs_fileattr_get(ffile->f_path.dentry, &fa)) + return -EOPNOTSUPP; + if (fa.fsx_xflags & FS_XFLAG_REALTIME) + return -EOPNOTSUPP; + return 0; +} + +static unsigned int fgds_file_align(struct inode *inode, bool write) +{ + unsigned int lbs = bdev_logical_block_size(inode->i_sb->s_bdev); + + if (!write) + return lbs; + return max(1U << inode->i_blkbits, lbs); +} + +static unsigned int fgds_dma_align(struct inode *inode) +{ + return bdev_dma_alignment(inode->i_sb->s_bdev) + 1; +} + +static struct fgds_reg *fgds_reg_get(struct fgds_state *state, u32 handle) +{ + struct fgds_reg *reg; + + mutex_lock(&state->lock); + reg = xa_load(&state->regs, handle); + if (reg) + kref_get(®->kref); + mutex_unlock(&state->lock); + return reg; +} + +static int fgds_unregister_handle(struct fgds_state *state, u32 handle) +{ + struct fgds_reg *reg; + + mutex_lock(&state->lock); + reg = xa_erase(&state->regs, handle); + mutex_unlock(&state->lock); + if (reg) + fgds_reg_put(reg); + return reg ? 0 : -EINVAL; +} + +static int fgds_register(struct file *file, struct fgds_register_args __user *uarg) +{ + struct fgds_register_args args; + struct fgds_state *state = fgds_state(file); + struct dma_buf_io_ctx *ctx = NULL; + struct block_device *bdev; + struct file *bdev_file; + struct dma_buf *dbuf; + struct fd bfdf; + struct fgds_reg *reg; + u32 id; + int ret; + + if (copy_from_user(&args, uarg, sizeof(args))) + return -EFAULT; + if (args.handle || args.pad || args.reserved) + return -EINVAL; + + dbuf = dma_buf_get(args.dfd); + if (IS_ERR(dbuf)) + return PTR_ERR(dbuf); + + bfdf = fdget(args.bdev_fd); + if (fd_empty(bfdf)) { + ret = -EBADF; + goto out_dmabuf; + } + bdev_file = fd_file(bfdf); + if (!S_ISBLK(file_inode(bdev_file)->i_mode)) { + ret = -EINVAL; + goto out_fdput; + } + /* blkdev_init_dma_buf_io_ctx() needs O_DIRECT. */ + if (!(bdev_file->f_flags & O_DIRECT)) { + ret = -EINVAL; + goto out_fdput; + } + + bdev = file_bdev(bdev_file); + if (!bdev->bd_disk->fops->init_dma_buf_io_ctx) { + ret = -EOPNOTSUPP; + goto out_fdput; + } + + ret = -ENOMEM; + reg = kzalloc_obj(*reg); + if (!reg) + goto out_fdput; + + get_file(bdev_file); + + ret = dma_buf_io_ctx_create(bdev_file, dbuf, DMA_BIDIRECTIONAL, &ctx); + if (ret) { + dev_err(fgds_misc.this_device, + "dma-buf-io context on %pg failed: %pe\n", + bdev, ERR_PTR(ret)); + goto out_file; + } + + kref_init(®->kref); + reg->ctx = ctx; + reg->bdev_file = bdev_file; + reg->bdev = bdev; + reg->size = dbuf->size; + + mutex_lock(&state->lock); + ret = xa_alloc(&state->regs, &id, reg, XA_LIMIT(1, UINT_MAX), + GFP_KERNEL); + if (ret) { + mutex_unlock(&state->lock); + goto out_ctx; + } + + args.handle = id; + /* the window must stay inside the buffer and within 32 bits */ + args.max_doffset = min_t(u64, reg->size, UINT_MAX); + + if (copy_to_user(uarg, &args, sizeof(args))) { + xa_erase(&state->regs, id); + mutex_unlock(&state->lock); + ret = -EFAULT; + goto out_ctx; + } + mutex_unlock(&state->lock); + + fdput(bfdf); + dma_buf_put(dbuf); + return 0; + +out_ctx: + dma_buf_io_ctx_release(ctx); +out_file: + fput(bdev_file); + kfree(reg); +out_fdput: + fdput(bfdf); +out_dmabuf: + dma_buf_put(dbuf); + return ret; +} + +static int fgds_unregister(struct file *file, struct fgds_unregister_args __user *uarg) +{ + struct fgds_unregister_args args; + + if (copy_from_user(&args, uarg, sizeof(args))) + return -EFAULT; + if (args.reserved) + return -EINVAL; + return fgds_unregister_handle(fgds_state(file), args.handle); +} + +/* + * Launch one read_iter()/write_iter() call. + * Returns the number of bytes the file system moved, or a negative + * errno; a short read means the end of the file. + */ +static ssize_t fgds_do_rw(struct fgds_state *state, + const struct fgds_rw_args *args, bool write) +{ + struct fgds_reg *reg; + struct dma_buf_io_map *map; + struct file *ffile; + struct inode *inode; + struct iov_iter iter; + struct kiocb kiocb; + struct fd fdf; + ssize_t (*iter_fn)(struct kiocb *kiocb, struct iov_iter *i); + unsigned int file_align, dma_align; + u64 length = args->length; + u64 dbuf_end; + loff_t offset, end; + ssize_t ret; + + if (args->flags) + return -EINVAL; + if (length > MAX_RW_COUNT) + return -EINVAL; + + offset = args->offset; + if (offset < 0 || check_add_overflow(offset, (loff_t)length, &end)) + return -EINVAL; + /* + * The block layer's dma-buf iterator truncates the segment offset to + * 32 bits, and the window must stay inside the registered buffer. + */ + if (check_add_overflow(args->doffset, length, &dbuf_end)) + return -EINVAL; + if (dbuf_end > UINT_MAX) + return -EINVAL; + + fdf = fdget(args->ffd); + if (fd_empty(fdf)) + return -EBADF; + ffile = fd_file(fdf); + inode = file_inode(ffile); + + ret = -EINVAL; + if (!S_ISREG(inode->i_mode) || (ffile->f_flags & O_APPEND)) + goto out_fdput; + + if (write) { + if (!(ffile->f_mode & FMODE_WRITE) || + !(ffile->f_mode & FMODE_CAN_WRITE)) + goto out_fdput; + iter_fn = ffile->f_op->write_iter; + } else { + if (!(ffile->f_mode & FMODE_READ) || + !(ffile->f_mode & FMODE_CAN_READ)) + goto out_fdput; + iter_fn = ffile->f_op->read_iter; + } + if (!iter_fn) { + ret = -EOPNOTSUPP; + goto out_fdput; + } + + ret = fgds_gate(ffile); + if (ret) + goto out_fdput; + + reg = fgds_reg_get(state, args->handle); + if (!reg) { + ret = -EINVAL; + goto out_fdput; + } + + if (inode->i_sb->s_bdev != reg->bdev) { + ret = -EXDEV; + goto out_reg; + } + + file_align = fgds_file_align(inode, write); + dma_align = fgds_dma_align(inode); + if ((offset | (loff_t)length) & (file_align - 1) || + (args->doffset | (loff_t)length) & (dma_align - 1) || + dbuf_end > reg->size || + (!write && end > i_size_read(inode))) { + ret = -EINVAL; + goto out_reg; + } + + ret = rw_verify_area(write ? WRITE : READ, ffile, &offset, length); + if (ret) + goto out_reg; + if (write) + inc_syscw(current); + else + inc_syscr(current); + + if (!length) { + ret = 0; + goto out_reg; + } + + map = dma_buf_io_get_map(reg->ctx, false); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + if (ret != -EAGAIN) + dev_err_ratelimited(fgds_misc.this_device, + "dma-buf mapping failed on %pg: %pe\n", + reg->bdev, ERR_PTR(ret)); + goto out_reg; + } + + /* + * A direct I/O read would leave any cached pages of the range + * stale, so flush and drop them before submitting. + */ + if (!write) { + ret = filemap_write_and_wait_range(ffile->f_mapping, + offset, end - 1); + if (ret) + goto out_map; + ret = invalidate_inode_pages2_range(ffile->f_mapping, + offset >> PAGE_SHIFT, + (end - 1) >> PAGE_SHIFT); + if (ret) { + /* + * Report a retryable error instead of letting + * -EBUSY escape. + */ + if (ret == -EBUSY) + ret = -EAGAIN; + goto out_map; + } + } + + if (write) + file_start_write(ffile); + iov_iter_dmabuf_map(&iter, write ? ITER_SOURCE : ITER_DEST, + map, args->doffset, length); + init_sync_kiocb(&kiocb, ffile); + kiocb.ki_flags |= IOCB_DIRECT; + if (write) + kiocb.ki_flags |= IOCB_WRITE; + kiocb.ki_pos = offset; + + ret = iter_fn(&kiocb, &iter); + if (WARN_ON_ONCE(ret == -EIOCBQUEUED)) + ret = -EIO; + if (write) + file_end_write(ffile); + + if (ret > 0) { + if (write) { + fsnotify_modify(ffile); + add_wchar(current, ret); + } else { + fsnotify_access(ffile); + add_rchar(current, ret); + } + } + +out_map: + dma_buf_io_map_drop(map); +out_reg: + fgds_reg_put(reg); +out_fdput: + fdput(fdf); + return ret; +} + +static long fgds_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct fgds_state *state = fgds_state(file); + struct fgds_rw_args args; + + switch (cmd) { + case FGDS_IOCTL_WRITE: + case FGDS_IOCTL_READ: + if (copy_from_user(&args, (void __user *)arg, sizeof(args))) + return -EFAULT; + return fgds_do_rw(state, &args, cmd == FGDS_IOCTL_WRITE); + case FGDS_IOCTL_REGISTER_BUF: + return fgds_register(file, (struct fgds_register_args __user *)arg); + case FGDS_IOCTL_UNREGISTER_BUF: + return fgds_unregister(file, (struct fgds_unregister_args __user *)arg); + } + return -ENOTTY; +} + +static int fgds_open(struct inode *inode, struct file *file) +{ + struct fgds_state *state; + + state = kzalloc_obj(*state); + if (!state) + return -ENOMEM; + + xa_init_flags(&state->regs, XA_FLAGS_ALLOC); + mutex_init(&state->lock); + file->private_data = state; + return 0; +} + +static int fgds_release(struct inode *inode, struct file *file) +{ + struct fgds_state *state = fgds_state(file); + struct fgds_reg *reg; + unsigned long id; + + xa_for_each(&state->regs, id, reg) { + xa_erase(&state->regs, id); + fgds_reg_put(reg); + } + xa_destroy(&state->regs); + mutex_destroy(&state->lock); + kfree(state); + return 0; +} + +static const struct file_operations fgds_fops = { + .owner = THIS_MODULE, + .open = fgds_open, + .release = fgds_release, + .unlocked_ioctl = fgds_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +static struct miscdevice fgds_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "fgds", + .fops = &fgds_fops, +}; + +static int __init fgds_init(void) +{ + return misc_register(&fgds_misc); +} + +static void __exit fgds_exit(void) +{ + misc_deregister(&fgds_misc); +} + +module_init(fgds_init); +module_exit(fgds_exit); + +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("DMA_BUF"); +MODULE_AUTHOR("Mengmeng Zhao <zhaomengmeng@kylinos.cn>, Li Wang <liwang@kylinos.cn>"); +MODULE_DESCRIPTION("Fast GPU Direct Storage"); +MODULE_VERSION("1.0.0"); diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index 0a5ebfda90f1..a7ab28edab98 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -1304,6 +1304,17 @@ iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *i, }; ssize_t ret; + /* + * The copy helpers have no source for a dma-buf iterator: the copy + * would move zero bytes, ->write_end() would reject the untouched + * folio, fault_in_iov_iter_readable() reports the iterator as + * readable, and the loop below would never advance. Refuse here + * rather than at each caller's entry point, so no path can reach + * the copy with such an iterator. + */ + if (iov_iter_is_dmabuf_map(i)) + return -EOPNOTSUPP; + if (iocb->ki_flags & IOCB_NOWAIT) iter.flags |= IOMAP_NOWAIT; if (iocb->ki_flags & IOCB_DONTCACHE) diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c index 8b4039d16ce8..eccc07d53e9c 100644 --- a/fs/iomap/direct-io.c +++ b/fs/iomap/direct-io.c @@ -595,8 +595,16 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) static int iomap_dio_hole_iter(struct iomap_iter *iter, struct iomap_dio *dio) { - loff_t length = iov_iter_zero(iomap_length(iter), dio->submit.iter); + loff_t length; + /* + * dmabuf maps do not support zero-filling holes or + * unwritten extents. + */ + if (iov_iter_is_dmabuf_map(dio->submit.iter)) + return -EOPNOTSUPP; + + length = iov_iter_zero(iomap_length(iter), dio->submit.iter); dio->size += length; if (!length) return -EFAULT; @@ -683,8 +691,8 @@ static int iomap_dio_iter(struct iomap_iter *iter, struct iomap_dio *dio) * the correct total number of bytes transferred; this is essential for * completing partial requests asynchronously. * - * Returns -ENOTBLK In case of a page invalidation invalidation failure for - * writes. The callers needs to fall back to buffered I/O in this case. + * Returns -ENOTBLK in case of a page cache invalidation failure for writes. + * The caller needs to fall back to buffered I/O in this case. */ struct iomap_dio * __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, @@ -794,6 +802,15 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, * the caller tries again. */ ret = -EAGAIN; + } else if (iov_iter_is_dmabuf_map(iter)) { + /* + * Avoid -ENOTBLK (buffered fallback) + * as dmabuf are not supported. Map + * -EBUSY to -EAGAIN for transient + * retires. + */ + if (ret == -EBUSY) + ret = -EAGAIN; } else { /* fall back to buffered write */ ret = -ENOTBLK; diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 426a67b813a7..f16e9cb63e9e 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1211,6 +1211,10 @@ xfs_file_write_iter( return ret; } + /* zoned and buffered I/O is not supported for dmabuf */ + if (iov_iter_is_dmabuf_map(from)) + return -EOPNOTSUPP; + if (xfs_is_zoned_inode(ip)) return xfs_file_buffered_write_zoned(iocb, from); return xfs_file_buffered_write(iocb, from); diff --git a/include/linux/iov_iter.h b/include/linux/iov_iter.h index 2f91884d733b..a3b99e6d8255 100644 --- a/include/linux/iov_iter.h +++ b/include/linux/iov_iter.h @@ -376,6 +376,10 @@ size_t iterate_and_advance_kernel(struct iov_iter *iter, size_t len, void *priv, return iterate_folioq(iter, len, priv, priv2, step); if (iov_iter_is_xarray(iter)) return iterate_xarray(iter, len, priv, priv2, step); + if (iov_iter_is_dmabuf_map(iter)) { + WARN_ON_ONCE(1); + return 0; + } return iterate_discard(iter, len, priv, priv2, step); } diff --git a/include/uapi/linux/fgds.h b/include/uapi/linux/fgds.h new file mode 100644 index 000000000000..9a721c92cde2 --- /dev/null +++ b/include/uapi/linux/fgds.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Fast GPU Direct Storage user-kernel ABI definitions. + * + * Copyright (C) 2026 KylinSoft. Co., Ltd. All rights reserved. + * + * Synchronous read/write ioctls that move data between a GPU dma-buf and + * a regular file. + */ +#ifndef _FGDS_H +#define _FGDS_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +/** + * struct fgds_register_args - arguments for %FGDS_IOCTL_REGISTER_BUF + * + * @dfd: dma-buf file descriptor exported by the GPU runtime. + * @bdev_fd: file descriptor of the underlying block device (the + * device the target files live on), opened with %O_DIRECT. + * The device is named separately from the file because: + * building the dma-buf I/O context needs a file for the + * device itself, and the device side requires %O_DIRECT + * on that file, which a regular file descriptor cannot + * provide. + * @handle: Returned handle for use with the other ioctls. Handles + * are private to the /dev/fgds file descriptor they were + * created on and are released automatically when that + * descriptor is closed. Output. + * @pad: Must be 0. + * @max_doffset: Largest accepted value of @doffset plus @length: the + * window must stay inside the registered dma-buf and + * within the 32-bit offset bound of the block-layer + * dma-buf iterator, so this reports the smaller of the + * dma-buf size and %UINT_MAX. Output. + * @reserved: Must be 0. Extension slot for further output fields. + * + * A registered handle keeps the device attachment alive, and once a + * transfer has mapped the buffer that mapping is held for the handle's + * lifetime. + * + * Returns 0, or a negative errno. + */ +struct fgds_register_args { + __s32 dfd; + __s32 bdev_fd; + __u32 handle; + __u32 pad; + __u64 max_doffset; + __u64 reserved; +}; + +/** + * struct fgds_unregister_args - arguments for %FGDS_IOCTL_UNREGISTER_BUF + * + * @handle: Handle returned by %FGDS_IOCTL_REGISTER_BUF. Input. + * @reserved: Must be 0. + * + * Returns 0, or -EINVAL if @handle is not registered on this file + * descriptor. + */ +struct fgds_unregister_args { + __u32 handle; + __u32 reserved; +}; + +/** + * struct fgds_rw_args - arguments for %FGDS_IOCTL_WRITE / %FGDS_IOCTL_READ + * + * @handle: Handle from %FGDS_IOCTL_REGISTER_BUF naming both the + * dma-buf and the block device to transfer between. Input. + * @ffd: Regular file descriptor. %O_DIRECT is not required on + * it, fgds forces the direct I/O path itself. Its file + * system's device must match the one registered with + * @handle or the transfer fails with %EXDEV. Input. + * @offset: Byte offset inside the file. Input. + * @length: Transfer length in bytes. Unlike pread() and pwrite(), + * which silently truncate an oversized request, fgds rejects + * it with %EINVAL. Input. + * @doffset: Byte offset inside the dma-buf, i.e. the start of the + * window the transfer reads from or writes to. Input. + * @flags: Must be 0. Input. + * + * Like pread() and pwrite(), the ioctl returns the number of bytes + * transferred or a negative errno. A return value below @length means a + * read stopped at the end of the file, or a write clamped by the calling + * process' RLIMIT_FSIZE. + * + * Contract and failure modes: + * + * - Alignment is enforced on two independent axes, both answered by + * statx() with %STATX_DIOALIGN. The file side applies to @offset and + * @length: the device logical block size for a read, the max of that + * and the file system block size for a write. The buffer side applies + * to @doffset and @length: the device DMA alignment. A transfer must + * satisfy both axes at once. + * - Ranges backed by file holes or unwritten extents are not supported + * for reads and fail with %EOPNOTSUPP: there is no page cache bounce + * to serve zeros from, and GPU memory is generally not CPU accessible. + * - Writes past the end of the file go through the file system's own + * direct I/O extend path; no preallocation is performed by fgds. An + * application that wants the space reserved must call fallocate() + * itself first. + * - Reads flush and drop any page cache of the transferred range before + * submitting, so subsequent buffered reads observe the fresh data. + */ +struct fgds_rw_args { + __u32 handle; + __s32 ffd; + __u64 offset; + __u64 length; + __u64 doffset; + __u64 flags; +}; + +#define FGDS_IOCTL_MAGIC 'x' + +#define FGDS_IOCTL_WRITE _IOW(FGDS_IOCTL_MAGIC, 1, struct fgds_rw_args) +#define FGDS_IOCTL_READ _IOW(FGDS_IOCTL_MAGIC, 2, struct fgds_rw_args) +#define FGDS_IOCTL_REGISTER_BUF _IOWR(FGDS_IOCTL_MAGIC, 3, struct fgds_register_args) +#define FGDS_IOCTL_UNREGISTER_BUF _IOW(FGDS_IOCTL_MAGIC, 4, struct fgds_unregister_args) + +#endif /* _FGDS_H */ diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 9b46f9ada8f9..b19bf7f5e4cf 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -790,6 +790,7 @@ void iov_iter_dmabuf_map(struct iov_iter *i, unsigned int direction, .iov_offset = off, }; } +EXPORT_SYMBOL_GPL(iov_iter_dmabuf_map); /** * iov_iter_discard - Initialise an I/O iterator that discards data diff --git a/mm/filemap.c b/mm/filemap.c index 6afec636881f..ab8a5391a699 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -3013,6 +3013,14 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) return retval; } + /* + * Buffered I/O read is not supported for dmabuf; a short direct + * read before this point stands on its own, as in + * direct_write_fallback(). + */ + if (iov_iter_is_dmabuf_map(iter)) + return retval > 0 ? retval : -EOPNOTSUPP; + return filemap_read(iocb, iter, retval); } EXPORT_SYMBOL(generic_file_read_iter); @@ -4300,8 +4308,14 @@ generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from) */ written = kiocb_invalidate_pages(iocb, write_len); if (written) { - if (written == -EBUSY) + if (written == -EBUSY) { + /* + * Avoid fallback to buffered write for dmabuf. + */ + if (iov_iter_is_dmabuf_map(from)) + return -EAGAIN; return 0; + } return written; } @@ -4353,6 +4367,12 @@ ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i) long status = 0; ssize_t written = 0; + /* + * Buffered write is not supported for dmabuf. + */ + if (iov_iter_is_dmabuf_map(i)) + return -EOPNOTSUPP; + do { struct folio *folio; size_t offset; /* Offset into folio */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [RFC PATCH v2 1/1] misc: fgds: introduce GPU-File direct I/O via dma-buf 2026-10-27 9:08 ` [RFC PATCH v2 1/1] " Li Wang @ 2026-10-27 13:03 ` Jens Axboe 2026-10-27 13:12 ` Greg Kroah-Hartman 2026-10-27 13:12 ` Christian König 1 sibling, 1 reply; 13+ messages in thread From: Jens Axboe @ 2026-10-27 13:03 UTC (permalink / raw) To: Li Wang, Arnd Bergmann, Greg Kroah-Hartman Cc: Sumit Semwal, Christian König, Pavel Begunkov, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On 10/27/26 3:08 AM, Li Wang wrote: > From: Mengmeng Zhao <zhaomengmeng@kylinos.cn> > > This patch introduces a driver that allows moving data between a GPU > dma-buf and a regular file on NVME without staging it through the page > cache. Userspace registers the GPU dma-buf with the device, then issues > read and write requests via the device's ioctls. Each transfer is direct > I/O: an ITER_DMABUF_MAP iterator is passed to the file's read_iter or > write_iter, and the filesystem's iomap path submits REQ_DMABUF bios that > already carry the dma addresses. > > Signed-off-by: Mengmeng Zhao <zhaomengmeng@kylinos.cn> > Signed-off-by: Li Wang <liwang@kylinos.cn> > --- > .../userspace-api/ioctl/ioctl-number.rst | 2 + > drivers/dma-buf/dma-buf-io.c | 3 + > drivers/misc/Kconfig | 16 + > drivers/misc/Makefile | 1 + > drivers/misc/fgds.c | 506 ++++++++++++++++++ > fs/iomap/buffered-io.c | 11 + > fs/iomap/direct-io.c | 23 +- > fs/xfs/xfs_file.c | 4 + > include/linux/iov_iter.h | 4 + > include/uapi/linux/fgds.h | 124 +++++ > lib/iov_iter.c | 1 + > mm/filemap.c | 22 +- > 12 files changed, 713 insertions(+), 4 deletions(-) > create mode 100644 drivers/misc/fgds.c > create mode 100644 include/uapi/linux/fgds.h What's the point of this, when you can just use io_uring to do the same?! -- Jens Axboe ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH v2 1/1] misc: fgds: introduce GPU-File direct I/O via dma-buf 2026-10-27 13:03 ` Jens Axboe @ 2026-10-27 13:12 ` Greg Kroah-Hartman 0 siblings, 0 replies; 13+ messages in thread From: Greg Kroah-Hartman @ 2026-10-27 13:12 UTC (permalink / raw) To: Jens Axboe Cc: Li Wang, Arnd Bergmann, Sumit Semwal, Christian König, Pavel Begunkov, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On Tue, Oct 27, 2026 at 07:03:29AM -0600, Jens Axboe wrote: > On 10/27/26 3:08 AM, Li Wang wrote: > > From: Mengmeng Zhao <zhaomengmeng@kylinos.cn> > > > > This patch introduces a driver that allows moving data between a GPU > > dma-buf and a regular file on NVME without staging it through the page > > cache. Userspace registers the GPU dma-buf with the device, then issues > > read and write requests via the device's ioctls. Each transfer is direct > > I/O: an ITER_DMABUF_MAP iterator is passed to the file's read_iter or > > write_iter, and the filesystem's iomap path submits REQ_DMABUF bios that > > already carry the dma addresses. > > > > Signed-off-by: Mengmeng Zhao <zhaomengmeng@kylinos.cn> > > Signed-off-by: Li Wang <liwang@kylinos.cn> > > --- > > .../userspace-api/ioctl/ioctl-number.rst | 2 + > > drivers/dma-buf/dma-buf-io.c | 3 + > > drivers/misc/Kconfig | 16 + > > drivers/misc/Makefile | 1 + > > drivers/misc/fgds.c | 506 ++++++++++++++++++ > > fs/iomap/buffered-io.c | 11 + > > fs/iomap/direct-io.c | 23 +- > > fs/xfs/xfs_file.c | 4 + > > include/linux/iov_iter.h | 4 + > > include/uapi/linux/fgds.h | 124 +++++ > > lib/iov_iter.c | 1 + > > mm/filemap.c | 22 +- > > 12 files changed, 713 insertions(+), 4 deletions(-) > > create mode 100644 drivers/misc/fgds.c > > create mode 100644 include/uapi/linux/fgds.h > > What's the point of this, when you can just use io_uring to do > the same?! Agreed, I thought that the summary of the last round of reviews was "this isn't needed at all". Li, what changed from that? thanks, greg k-h ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH v2 1/1] misc: fgds: introduce GPU-File direct I/O via dma-buf 2026-10-27 9:08 ` [RFC PATCH v2 1/1] " Li Wang 2026-10-27 13:03 ` Jens Axboe @ 2026-10-27 13:12 ` Christian König 1 sibling, 0 replies; 13+ messages in thread From: Christian König @ 2026-10-27 13:12 UTC (permalink / raw) To: Li Wang, Arnd Bergmann, Greg Kroah-Hartman Cc: Sumit Semwal, Pavel Begunkov, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao On 10/27/26 10:08, Li Wang wrote: > From: Mengmeng Zhao <zhaomengmeng@kylinos.cn> > > This patch introduces a driver that allows moving data between a GPU > dma-buf and a regular file on NVME without staging it through the page > cache. Userspace registers the GPU dma-buf with the device, then issues > read and write requests via the device's ioctls. Each transfer is direct > I/O: an ITER_DMABUF_MAP iterator is passed to the file's read_iter or > write_iter, and the filesystem's iomap path submits REQ_DMABUF bios that > already carry the dma addresses. Why in the world is that still useful with Pavel work on io_uring? > > Signed-off-by: Mengmeng Zhao <zhaomengmeng@kylinos.cn> > Signed-off-by: Li Wang <liwang@kylinos.cn> > --- > .../userspace-api/ioctl/ioctl-number.rst | 2 + > drivers/dma-buf/dma-buf-io.c | 3 + > drivers/misc/Kconfig | 16 + > drivers/misc/Makefile | 1 + > drivers/misc/fgds.c | 506 ++++++++++++++++++ > fs/iomap/buffered-io.c | 11 + > fs/iomap/direct-io.c | 23 +- > fs/xfs/xfs_file.c | 4 + > include/linux/iov_iter.h | 4 + > include/uapi/linux/fgds.h | 124 +++++ > lib/iov_iter.c | 1 + > mm/filemap.c | 22 +- > 12 files changed, 713 insertions(+), 4 deletions(-) > create mode 100644 drivers/misc/fgds.c > create mode 100644 include/uapi/linux/fgds.h > > diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst > index 2fc53093752d..5f93a252e773 100644 > --- a/Documentation/userspace-api/ioctl/ioctl-number.rst > +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst > @@ -306,6 +306,8 @@ Code Seq# Include File Comments > 'v' 20-27 arch/powerpc/include/uapi/asm/vas-api.h VAS API > 'v' C0-FF linux/meye.h conflict! > 'w' all CERN SCI driver > +'x' 00-0F linux/fgds.h Fast GPU Direct Storage > + <mailto:liwang@kylinos.cn> > 'y' 00-1F packet based user level communications > <mailto:zapman@interlan.net> > 'z' 00-3F CAN bus card conflict! > diff --git a/drivers/dma-buf/dma-buf-io.c b/drivers/dma-buf/dma-buf-io.c > index 8440151de860..2ee4fc683c74 100644 > --- a/drivers/dma-buf/dma-buf-io.c > +++ b/drivers/dma-buf/dma-buf-io.c > @@ -135,6 +135,7 @@ struct dma_buf_io_map *dma_buf_io_create_map(struct dma_buf_io_ctx *ctx) > dma_resv_unlock(ctx->dmabuf->resv); > return map; > } > +EXPORT_SYMBOL_NS_GPL(dma_buf_io_create_map, "DMA_BUF"); Clear NAK to that. Those are FS layer helper functionalities for DMA-buf and not something drivers should ever need to mess with. Regards, Christian. > > static void dma_buf_io_ctx_shutdown(struct dma_buf_io_ctx *ctx) > { > @@ -190,6 +191,7 @@ int dma_buf_io_ctx_create(struct file *file, > *out_ctx = ctx; > return 0; > } > +EXPORT_SYMBOL_NS_GPL(dma_buf_io_ctx_create, "DMA_BUF"); > > /** > * dma_buf_io_ctx_release - Destroy the I/O context > @@ -211,6 +213,7 @@ void dma_buf_io_ctx_release(struct dma_buf_io_ctx *ctx) > dma_buf_put(ctx->dmabuf); > kfree(ctx); > } > +EXPORT_SYMBOL_NS_GPL(dma_buf_io_ctx_release, "DMA_BUF"); > > /** > * dma_buf_io_invalidate_mappings - Invalidate the current map > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index 7364931dad3a..b85fd64d27ad 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -568,6 +568,22 @@ config MCHP_LAN966X_PCI > - lan966x-miim (MDIO_MSCC_MIIM) > - lan966x-switch (LAN966X_SWITCH) > > +config FGDS > + tristate "Fast GPU Direct Storage support" > + depends on DMA_SHARED_BUFFER && BLOCK > + default n > + help > + This driver exposes a misc device that moves data between a GPU > + dma-buf and a regular file with synchronous read/write ioctls, > + submitting the transfer as direct I/O (REQ_DMABUF bios) through > + the file system's iomap DIO path without a page cache bounce > + buffer. > + > + To compile this driver as a module, choose M here: the module > + will be called fgds. > + > + If unsure, say N. > + > source "drivers/misc/c2port/Kconfig" > source "drivers/misc/eeprom/Kconfig" > source "drivers/misc/cb710/Kconfig" > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index e8d8d5d88c0d..cd98ef3d008e 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -70,4 +70,5 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o > obj-y += keba/ > obj-y += amd-sbi/ > obj-$(CONFIG_MISC_RP1) += rp1/ > +obj-$(CONFIG_FGDS) += fgds.o > obj-$(CONFIG_INTEL_SSEI) += issei/ > diff --git a/drivers/misc/fgds.c b/drivers/misc/fgds.c > new file mode 100644 > index 000000000000..3929a6514d23 > --- /dev/null > +++ b/drivers/misc/fgds.c > @@ -0,0 +1,506 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Fast GPU Direct Storage via dma-buf. > + * > + * Copyright (C) 2026 KylinSoft. Co., Ltd. All rights reserved. > + * > + * Synchronous read/write ioctls that move data between a GPU dma-buf and > + * a regular file. > + */ > + > +#include <linux/blkdev.h> > +#include <linux/dma-buf.h> > +#include <linux/dma-buf-io.h> > +#include <linux/device.h> > +#include <linux/fcntl.h> > +#include <linux/fileattr.h> > +#include <linux/fs.h> > +#include <linux/fsnotify.h> > +#include <linux/magic.h> > +#include <linux/math.h> > +#include <linux/miscdevice.h> > +#include <linux/module.h> > +#include <linux/overflow.h> > +#include <linux/pagemap.h> > +#include <linux/sched/xacct.h> > +#include <linux/slab.h> > +#include <linux/uaccess.h> > +#include <linux/uio.h> > +#include <linux/xarray.h> > + > +#include <linux/fgds.h> > + > +static struct miscdevice fgds_misc; > + > +/* One registered buffer: a dma-buf bound to a block device. */ > +struct fgds_reg { > + struct kref kref; > + struct dma_buf_io_ctx *ctx; > + struct file *bdev_file; > + struct block_device *bdev; > + u64 size; > +}; > + > +/* State of one /dev/fgds open file description; handles are private to it. */ > +struct fgds_state { > + struct xarray regs; > + /* protects @regs and, with the reg kref, the lifetime of entries */ > + struct mutex lock; > +}; > + > +static void fgds_reg_release(struct kref *kref) > +{ > + struct fgds_reg *reg = container_of(kref, struct fgds_reg, kref); > + > + dma_buf_io_ctx_release(reg->ctx); > + fput(reg->bdev_file); > + kfree(reg); > +} > + > +static void fgds_reg_put(struct fgds_reg *reg) > +{ > + kref_put(®->kref, fgds_reg_release); > +} > + > +static struct fgds_state *fgds_state(struct file *file) > +{ > + return file->private_data; > +} > + > +/* > + * File and file-system level gate. > + */ > +static int fgds_gate(struct file *ffile) > +{ > + struct inode *inode = file_inode(ffile); > + struct super_block *sb = inode->i_sb; > + struct file_kattr fa; > + > + if (!S_ISREG(inode->i_mode)) > + return -EINVAL; > + if (sb->s_magic != XFS_SUPER_MAGIC) > + return -EOPNOTSUPP; > + if (IS_DAX(inode)) > + return -EOPNOTSUPP; > + if (mapping_stable_writes(inode->i_mapping)) > + return -EOPNOTSUPP; > + if (!sb->s_bdev) > + return -EOPNOTSUPP; > + if (bdev_is_zoned(sb->s_bdev)) > + return -EOPNOTSUPP; > + if (!sb->s_bdev->bd_disk->fops->init_dma_buf_io_ctx) > + return -EOPNOTSUPP; > + > + /* > + * Realtime inodes live on a separate device whose DMA setup is not > + * covered here. > + */ > + if (vfs_fileattr_get(ffile->f_path.dentry, &fa)) > + return -EOPNOTSUPP; > + if (fa.fsx_xflags & FS_XFLAG_REALTIME) > + return -EOPNOTSUPP; > + return 0; > +} > + > +static unsigned int fgds_file_align(struct inode *inode, bool write) > +{ > + unsigned int lbs = bdev_logical_block_size(inode->i_sb->s_bdev); > + > + if (!write) > + return lbs; > + return max(1U << inode->i_blkbits, lbs); > +} > + > +static unsigned int fgds_dma_align(struct inode *inode) > +{ > + return bdev_dma_alignment(inode->i_sb->s_bdev) + 1; > +} > + > +static struct fgds_reg *fgds_reg_get(struct fgds_state *state, u32 handle) > +{ > + struct fgds_reg *reg; > + > + mutex_lock(&state->lock); > + reg = xa_load(&state->regs, handle); > + if (reg) > + kref_get(®->kref); > + mutex_unlock(&state->lock); > + return reg; > +} > + > +static int fgds_unregister_handle(struct fgds_state *state, u32 handle) > +{ > + struct fgds_reg *reg; > + > + mutex_lock(&state->lock); > + reg = xa_erase(&state->regs, handle); > + mutex_unlock(&state->lock); > + if (reg) > + fgds_reg_put(reg); > + return reg ? 0 : -EINVAL; > +} > + > +static int fgds_register(struct file *file, struct fgds_register_args __user *uarg) > +{ > + struct fgds_register_args args; > + struct fgds_state *state = fgds_state(file); > + struct dma_buf_io_ctx *ctx = NULL; > + struct block_device *bdev; > + struct file *bdev_file; > + struct dma_buf *dbuf; > + struct fd bfdf; > + struct fgds_reg *reg; > + u32 id; > + int ret; > + > + if (copy_from_user(&args, uarg, sizeof(args))) > + return -EFAULT; > + if (args.handle || args.pad || args.reserved) > + return -EINVAL; > + > + dbuf = dma_buf_get(args.dfd); > + if (IS_ERR(dbuf)) > + return PTR_ERR(dbuf); > + > + bfdf = fdget(args.bdev_fd); > + if (fd_empty(bfdf)) { > + ret = -EBADF; > + goto out_dmabuf; > + } > + bdev_file = fd_file(bfdf); > + if (!S_ISBLK(file_inode(bdev_file)->i_mode)) { > + ret = -EINVAL; > + goto out_fdput; > + } > + /* blkdev_init_dma_buf_io_ctx() needs O_DIRECT. */ > + if (!(bdev_file->f_flags & O_DIRECT)) { > + ret = -EINVAL; > + goto out_fdput; > + } > + > + bdev = file_bdev(bdev_file); > + if (!bdev->bd_disk->fops->init_dma_buf_io_ctx) { > + ret = -EOPNOTSUPP; > + goto out_fdput; > + } > + > + ret = -ENOMEM; > + reg = kzalloc_obj(*reg); > + if (!reg) > + goto out_fdput; > + > + get_file(bdev_file); > + > + ret = dma_buf_io_ctx_create(bdev_file, dbuf, DMA_BIDIRECTIONAL, &ctx); > + if (ret) { > + dev_err(fgds_misc.this_device, > + "dma-buf-io context on %pg failed: %pe\n", > + bdev, ERR_PTR(ret)); > + goto out_file; > + } > + > + kref_init(®->kref); > + reg->ctx = ctx; > + reg->bdev_file = bdev_file; > + reg->bdev = bdev; > + reg->size = dbuf->size; > + > + mutex_lock(&state->lock); > + ret = xa_alloc(&state->regs, &id, reg, XA_LIMIT(1, UINT_MAX), > + GFP_KERNEL); > + if (ret) { > + mutex_unlock(&state->lock); > + goto out_ctx; > + } > + > + args.handle = id; > + /* the window must stay inside the buffer and within 32 bits */ > + args.max_doffset = min_t(u64, reg->size, UINT_MAX); > + > + if (copy_to_user(uarg, &args, sizeof(args))) { > + xa_erase(&state->regs, id); > + mutex_unlock(&state->lock); > + ret = -EFAULT; > + goto out_ctx; > + } > + mutex_unlock(&state->lock); > + > + fdput(bfdf); > + dma_buf_put(dbuf); > + return 0; > + > +out_ctx: > + dma_buf_io_ctx_release(ctx); > +out_file: > + fput(bdev_file); > + kfree(reg); > +out_fdput: > + fdput(bfdf); > +out_dmabuf: > + dma_buf_put(dbuf); > + return ret; > +} > + > +static int fgds_unregister(struct file *file, struct fgds_unregister_args __user *uarg) > +{ > + struct fgds_unregister_args args; > + > + if (copy_from_user(&args, uarg, sizeof(args))) > + return -EFAULT; > + if (args.reserved) > + return -EINVAL; > + return fgds_unregister_handle(fgds_state(file), args.handle); > +} > + > +/* > + * Launch one read_iter()/write_iter() call. > + * Returns the number of bytes the file system moved, or a negative > + * errno; a short read means the end of the file. > + */ > +static ssize_t fgds_do_rw(struct fgds_state *state, > + const struct fgds_rw_args *args, bool write) > +{ > + struct fgds_reg *reg; > + struct dma_buf_io_map *map; > + struct file *ffile; > + struct inode *inode; > + struct iov_iter iter; > + struct kiocb kiocb; > + struct fd fdf; > + ssize_t (*iter_fn)(struct kiocb *kiocb, struct iov_iter *i); > + unsigned int file_align, dma_align; > + u64 length = args->length; > + u64 dbuf_end; > + loff_t offset, end; > + ssize_t ret; > + > + if (args->flags) > + return -EINVAL; > + if (length > MAX_RW_COUNT) > + return -EINVAL; > + > + offset = args->offset; > + if (offset < 0 || check_add_overflow(offset, (loff_t)length, &end)) > + return -EINVAL; > + /* > + * The block layer's dma-buf iterator truncates the segment offset to > + * 32 bits, and the window must stay inside the registered buffer. > + */ > + if (check_add_overflow(args->doffset, length, &dbuf_end)) > + return -EINVAL; > + if (dbuf_end > UINT_MAX) > + return -EINVAL; > + > + fdf = fdget(args->ffd); > + if (fd_empty(fdf)) > + return -EBADF; > + ffile = fd_file(fdf); > + inode = file_inode(ffile); > + > + ret = -EINVAL; > + if (!S_ISREG(inode->i_mode) || (ffile->f_flags & O_APPEND)) > + goto out_fdput; > + > + if (write) { > + if (!(ffile->f_mode & FMODE_WRITE) || > + !(ffile->f_mode & FMODE_CAN_WRITE)) > + goto out_fdput; > + iter_fn = ffile->f_op->write_iter; > + } else { > + if (!(ffile->f_mode & FMODE_READ) || > + !(ffile->f_mode & FMODE_CAN_READ)) > + goto out_fdput; > + iter_fn = ffile->f_op->read_iter; > + } > + if (!iter_fn) { > + ret = -EOPNOTSUPP; > + goto out_fdput; > + } > + > + ret = fgds_gate(ffile); > + if (ret) > + goto out_fdput; > + > + reg = fgds_reg_get(state, args->handle); > + if (!reg) { > + ret = -EINVAL; > + goto out_fdput; > + } > + > + if (inode->i_sb->s_bdev != reg->bdev) { > + ret = -EXDEV; > + goto out_reg; > + } > + > + file_align = fgds_file_align(inode, write); > + dma_align = fgds_dma_align(inode); > + if ((offset | (loff_t)length) & (file_align - 1) || > + (args->doffset | (loff_t)length) & (dma_align - 1) || > + dbuf_end > reg->size || > + (!write && end > i_size_read(inode))) { > + ret = -EINVAL; > + goto out_reg; > + } > + > + ret = rw_verify_area(write ? WRITE : READ, ffile, &offset, length); > + if (ret) > + goto out_reg; > + if (write) > + inc_syscw(current); > + else > + inc_syscr(current); > + > + if (!length) { > + ret = 0; > + goto out_reg; > + } > + > + map = dma_buf_io_get_map(reg->ctx, false); > + if (IS_ERR(map)) { > + ret = PTR_ERR(map); > + if (ret != -EAGAIN) > + dev_err_ratelimited(fgds_misc.this_device, > + "dma-buf mapping failed on %pg: %pe\n", > + reg->bdev, ERR_PTR(ret)); > + goto out_reg; > + } > + > + /* > + * A direct I/O read would leave any cached pages of the range > + * stale, so flush and drop them before submitting. > + */ > + if (!write) { > + ret = filemap_write_and_wait_range(ffile->f_mapping, > + offset, end - 1); > + if (ret) > + goto out_map; > + ret = invalidate_inode_pages2_range(ffile->f_mapping, > + offset >> PAGE_SHIFT, > + (end - 1) >> PAGE_SHIFT); > + if (ret) { > + /* > + * Report a retryable error instead of letting > + * -EBUSY escape. > + */ > + if (ret == -EBUSY) > + ret = -EAGAIN; > + goto out_map; > + } > + } > + > + if (write) > + file_start_write(ffile); > + iov_iter_dmabuf_map(&iter, write ? ITER_SOURCE : ITER_DEST, > + map, args->doffset, length); > + init_sync_kiocb(&kiocb, ffile); > + kiocb.ki_flags |= IOCB_DIRECT; > + if (write) > + kiocb.ki_flags |= IOCB_WRITE; > + kiocb.ki_pos = offset; > + > + ret = iter_fn(&kiocb, &iter); > + if (WARN_ON_ONCE(ret == -EIOCBQUEUED)) > + ret = -EIO; > + if (write) > + file_end_write(ffile); > + > + if (ret > 0) { > + if (write) { > + fsnotify_modify(ffile); > + add_wchar(current, ret); > + } else { > + fsnotify_access(ffile); > + add_rchar(current, ret); > + } > + } > + > +out_map: > + dma_buf_io_map_drop(map); > +out_reg: > + fgds_reg_put(reg); > +out_fdput: > + fdput(fdf); > + return ret; > +} > + > +static long fgds_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > +{ > + struct fgds_state *state = fgds_state(file); > + struct fgds_rw_args args; > + > + switch (cmd) { > + case FGDS_IOCTL_WRITE: > + case FGDS_IOCTL_READ: > + if (copy_from_user(&args, (void __user *)arg, sizeof(args))) > + return -EFAULT; > + return fgds_do_rw(state, &args, cmd == FGDS_IOCTL_WRITE); > + case FGDS_IOCTL_REGISTER_BUF: > + return fgds_register(file, (struct fgds_register_args __user *)arg); > + case FGDS_IOCTL_UNREGISTER_BUF: > + return fgds_unregister(file, (struct fgds_unregister_args __user *)arg); > + } > + return -ENOTTY; > +} > + > +static int fgds_open(struct inode *inode, struct file *file) > +{ > + struct fgds_state *state; > + > + state = kzalloc_obj(*state); > + if (!state) > + return -ENOMEM; > + > + xa_init_flags(&state->regs, XA_FLAGS_ALLOC); > + mutex_init(&state->lock); > + file->private_data = state; > + return 0; > +} > + > +static int fgds_release(struct inode *inode, struct file *file) > +{ > + struct fgds_state *state = fgds_state(file); > + struct fgds_reg *reg; > + unsigned long id; > + > + xa_for_each(&state->regs, id, reg) { > + xa_erase(&state->regs, id); > + fgds_reg_put(reg); > + } > + xa_destroy(&state->regs); > + mutex_destroy(&state->lock); > + kfree(state); > + return 0; > +} > + > +static const struct file_operations fgds_fops = { > + .owner = THIS_MODULE, > + .open = fgds_open, > + .release = fgds_release, > + .unlocked_ioctl = fgds_ioctl, > + .compat_ioctl = compat_ptr_ioctl, > +}; > + > +static struct miscdevice fgds_misc = { > + .minor = MISC_DYNAMIC_MINOR, > + .name = "fgds", > + .fops = &fgds_fops, > +}; > + > +static int __init fgds_init(void) > +{ > + return misc_register(&fgds_misc); > +} > + > +static void __exit fgds_exit(void) > +{ > + misc_deregister(&fgds_misc); > +} > + > +module_init(fgds_init); > +module_exit(fgds_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS("DMA_BUF"); > +MODULE_AUTHOR("Mengmeng Zhao <zhaomengmeng@kylinos.cn>, Li Wang <liwang@kylinos.cn>"); > +MODULE_DESCRIPTION("Fast GPU Direct Storage"); > +MODULE_VERSION("1.0.0"); > diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c > index 0a5ebfda90f1..a7ab28edab98 100644 > --- a/fs/iomap/buffered-io.c > +++ b/fs/iomap/buffered-io.c > @@ -1304,6 +1304,17 @@ iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *i, > }; > ssize_t ret; > > + /* > + * The copy helpers have no source for a dma-buf iterator: the copy > + * would move zero bytes, ->write_end() would reject the untouched > + * folio, fault_in_iov_iter_readable() reports the iterator as > + * readable, and the loop below would never advance. Refuse here > + * rather than at each caller's entry point, so no path can reach > + * the copy with such an iterator. > + */ > + if (iov_iter_is_dmabuf_map(i)) > + return -EOPNOTSUPP; > + > if (iocb->ki_flags & IOCB_NOWAIT) > iter.flags |= IOMAP_NOWAIT; > if (iocb->ki_flags & IOCB_DONTCACHE) > diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c > index 8b4039d16ce8..eccc07d53e9c 100644 > --- a/fs/iomap/direct-io.c > +++ b/fs/iomap/direct-io.c > @@ -595,8 +595,16 @@ static int iomap_dio_bio_iter(struct iomap_iter *iter, struct iomap_dio *dio) > > static int iomap_dio_hole_iter(struct iomap_iter *iter, struct iomap_dio *dio) > { > - loff_t length = iov_iter_zero(iomap_length(iter), dio->submit.iter); > + loff_t length; > > + /* > + * dmabuf maps do not support zero-filling holes or > + * unwritten extents. > + */ > + if (iov_iter_is_dmabuf_map(dio->submit.iter)) > + return -EOPNOTSUPP; > + > + length = iov_iter_zero(iomap_length(iter), dio->submit.iter); > dio->size += length; > if (!length) > return -EFAULT; > @@ -683,8 +691,8 @@ static int iomap_dio_iter(struct iomap_iter *iter, struct iomap_dio *dio) > * the correct total number of bytes transferred; this is essential for > * completing partial requests asynchronously. > * > - * Returns -ENOTBLK In case of a page invalidation invalidation failure for > - * writes. The callers needs to fall back to buffered I/O in this case. > + * Returns -ENOTBLK in case of a page cache invalidation failure for writes. > + * The caller needs to fall back to buffered I/O in this case. > */ > struct iomap_dio * > __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, > @@ -794,6 +802,15 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, > * the caller tries again. > */ > ret = -EAGAIN; > + } else if (iov_iter_is_dmabuf_map(iter)) { > + /* > + * Avoid -ENOTBLK (buffered fallback) > + * as dmabuf are not supported. Map > + * -EBUSY to -EAGAIN for transient > + * retires. > + */ > + if (ret == -EBUSY) > + ret = -EAGAIN; > } else { > /* fall back to buffered write */ > ret = -ENOTBLK; > diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c > index 426a67b813a7..f16e9cb63e9e 100644 > --- a/fs/xfs/xfs_file.c > +++ b/fs/xfs/xfs_file.c > @@ -1211,6 +1211,10 @@ xfs_file_write_iter( > return ret; > } > > + /* zoned and buffered I/O is not supported for dmabuf */ > + if (iov_iter_is_dmabuf_map(from)) > + return -EOPNOTSUPP; > + > if (xfs_is_zoned_inode(ip)) > return xfs_file_buffered_write_zoned(iocb, from); > return xfs_file_buffered_write(iocb, from); > diff --git a/include/linux/iov_iter.h b/include/linux/iov_iter.h > index 2f91884d733b..a3b99e6d8255 100644 > --- a/include/linux/iov_iter.h > +++ b/include/linux/iov_iter.h > @@ -376,6 +376,10 @@ size_t iterate_and_advance_kernel(struct iov_iter *iter, size_t len, void *priv, > return iterate_folioq(iter, len, priv, priv2, step); > if (iov_iter_is_xarray(iter)) > return iterate_xarray(iter, len, priv, priv2, step); > + if (iov_iter_is_dmabuf_map(iter)) { > + WARN_ON_ONCE(1); > + return 0; > + } > return iterate_discard(iter, len, priv, priv2, step); > } > > diff --git a/include/uapi/linux/fgds.h b/include/uapi/linux/fgds.h > new file mode 100644 > index 000000000000..9a721c92cde2 > --- /dev/null > +++ b/include/uapi/linux/fgds.h > @@ -0,0 +1,124 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +/* > + * Fast GPU Direct Storage user-kernel ABI definitions. > + * > + * Copyright (C) 2026 KylinSoft. Co., Ltd. All rights reserved. > + * > + * Synchronous read/write ioctls that move data between a GPU dma-buf and > + * a regular file. > + */ > +#ifndef _FGDS_H > +#define _FGDS_H > + > +#include <linux/types.h> > +#include <linux/ioctl.h> > + > +/** > + * struct fgds_register_args - arguments for %FGDS_IOCTL_REGISTER_BUF > + * > + * @dfd: dma-buf file descriptor exported by the GPU runtime. > + * @bdev_fd: file descriptor of the underlying block device (the > + * device the target files live on), opened with %O_DIRECT. > + * The device is named separately from the file because: > + * building the dma-buf I/O context needs a file for the > + * device itself, and the device side requires %O_DIRECT > + * on that file, which a regular file descriptor cannot > + * provide. > + * @handle: Returned handle for use with the other ioctls. Handles > + * are private to the /dev/fgds file descriptor they were > + * created on and are released automatically when that > + * descriptor is closed. Output. > + * @pad: Must be 0. > + * @max_doffset: Largest accepted value of @doffset plus @length: the > + * window must stay inside the registered dma-buf and > + * within the 32-bit offset bound of the block-layer > + * dma-buf iterator, so this reports the smaller of the > + * dma-buf size and %UINT_MAX. Output. > + * @reserved: Must be 0. Extension slot for further output fields. > + * > + * A registered handle keeps the device attachment alive, and once a > + * transfer has mapped the buffer that mapping is held for the handle's > + * lifetime. > + * > + * Returns 0, or a negative errno. > + */ > +struct fgds_register_args { > + __s32 dfd; > + __s32 bdev_fd; > + __u32 handle; > + __u32 pad; > + __u64 max_doffset; > + __u64 reserved; > +}; > + > +/** > + * struct fgds_unregister_args - arguments for %FGDS_IOCTL_UNREGISTER_BUF > + * > + * @handle: Handle returned by %FGDS_IOCTL_REGISTER_BUF. Input. > + * @reserved: Must be 0. > + * > + * Returns 0, or -EINVAL if @handle is not registered on this file > + * descriptor. > + */ > +struct fgds_unregister_args { > + __u32 handle; > + __u32 reserved; > +}; > + > +/** > + * struct fgds_rw_args - arguments for %FGDS_IOCTL_WRITE / %FGDS_IOCTL_READ > + * > + * @handle: Handle from %FGDS_IOCTL_REGISTER_BUF naming both the > + * dma-buf and the block device to transfer between. Input. > + * @ffd: Regular file descriptor. %O_DIRECT is not required on > + * it, fgds forces the direct I/O path itself. Its file > + * system's device must match the one registered with > + * @handle or the transfer fails with %EXDEV. Input. > + * @offset: Byte offset inside the file. Input. > + * @length: Transfer length in bytes. Unlike pread() and pwrite(), > + * which silently truncate an oversized request, fgds rejects > + * it with %EINVAL. Input. > + * @doffset: Byte offset inside the dma-buf, i.e. the start of the > + * window the transfer reads from or writes to. Input. > + * @flags: Must be 0. Input. > + * > + * Like pread() and pwrite(), the ioctl returns the number of bytes > + * transferred or a negative errno. A return value below @length means a > + * read stopped at the end of the file, or a write clamped by the calling > + * process' RLIMIT_FSIZE. > + * > + * Contract and failure modes: > + * > + * - Alignment is enforced on two independent axes, both answered by > + * statx() with %STATX_DIOALIGN. The file side applies to @offset and > + * @length: the device logical block size for a read, the max of that > + * and the file system block size for a write. The buffer side applies > + * to @doffset and @length: the device DMA alignment. A transfer must > + * satisfy both axes at once. > + * - Ranges backed by file holes or unwritten extents are not supported > + * for reads and fail with %EOPNOTSUPP: there is no page cache bounce > + * to serve zeros from, and GPU memory is generally not CPU accessible. > + * - Writes past the end of the file go through the file system's own > + * direct I/O extend path; no preallocation is performed by fgds. An > + * application that wants the space reserved must call fallocate() > + * itself first. > + * - Reads flush and drop any page cache of the transferred range before > + * submitting, so subsequent buffered reads observe the fresh data. > + */ > +struct fgds_rw_args { > + __u32 handle; > + __s32 ffd; > + __u64 offset; > + __u64 length; > + __u64 doffset; > + __u64 flags; > +}; > + > +#define FGDS_IOCTL_MAGIC 'x' > + > +#define FGDS_IOCTL_WRITE _IOW(FGDS_IOCTL_MAGIC, 1, struct fgds_rw_args) > +#define FGDS_IOCTL_READ _IOW(FGDS_IOCTL_MAGIC, 2, struct fgds_rw_args) > +#define FGDS_IOCTL_REGISTER_BUF _IOWR(FGDS_IOCTL_MAGIC, 3, struct fgds_register_args) > +#define FGDS_IOCTL_UNREGISTER_BUF _IOW(FGDS_IOCTL_MAGIC, 4, struct fgds_unregister_args) > + > +#endif /* _FGDS_H */ > diff --git a/lib/iov_iter.c b/lib/iov_iter.c > index 9b46f9ada8f9..b19bf7f5e4cf 100644 > --- a/lib/iov_iter.c > +++ b/lib/iov_iter.c > @@ -790,6 +790,7 @@ void iov_iter_dmabuf_map(struct iov_iter *i, unsigned int direction, > .iov_offset = off, > }; > } > +EXPORT_SYMBOL_GPL(iov_iter_dmabuf_map); > > /** > * iov_iter_discard - Initialise an I/O iterator that discards data > diff --git a/mm/filemap.c b/mm/filemap.c > index 6afec636881f..ab8a5391a699 100644 > --- a/mm/filemap.c > +++ b/mm/filemap.c > @@ -3013,6 +3013,14 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) > return retval; > } > > + /* > + * Buffered I/O read is not supported for dmabuf; a short direct > + * read before this point stands on its own, as in > + * direct_write_fallback(). > + */ > + if (iov_iter_is_dmabuf_map(iter)) > + return retval > 0 ? retval : -EOPNOTSUPP; > + > return filemap_read(iocb, iter, retval); > } > EXPORT_SYMBOL(generic_file_read_iter); > @@ -4300,8 +4308,14 @@ generic_file_direct_write(struct kiocb *iocb, struct iov_iter *from) > */ > written = kiocb_invalidate_pages(iocb, write_len); > if (written) { > - if (written == -EBUSY) > + if (written == -EBUSY) { > + /* > + * Avoid fallback to buffered write for dmabuf. > + */ > + if (iov_iter_is_dmabuf_map(from)) > + return -EAGAIN; > return 0; > + } > return written; > } > > @@ -4353,6 +4367,12 @@ ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i) > long status = 0; > ssize_t written = 0; > > + /* > + * Buffered write is not supported for dmabuf. > + */ > + if (iov_iter_is_dmabuf_map(i)) > + return -EOPNOTSUPP; > + > do { > struct folio *folio; > size_t offset; /* Offset into folio */ ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH v2 0/1] misc: fgds: introduce GPU-File direct I/O via dma-buf 2026-10-27 9:08 ` [RFC PATCH v2 0/1] misc: fgds: introduce GPU-File direct I/O via dma-buf Li Wang 2026-10-27 9:08 ` [RFC PATCH v2 1/1] " Li Wang @ 2026-10-27 13:54 ` Pavel Begunkov 2026-10-28 3:01 ` Li Wang 1 sibling, 1 reply; 13+ messages in thread From: Pavel Begunkov @ 2026-10-27 13:54 UTC (permalink / raw) To: Li Wang, Arnd Bergmann, Greg Kroah-Hartman Cc: Sumit Semwal, Christian König, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel Hey Li, On 10/27/26 09:08, Li Wang wrote: > This patch introduces a driver named fgds that allows moving data between > a GPU dma-buf and a regular file on NVME without staging it through the > page cache. This is particularly useful for LLM workloads, such as model > loading, KV cache offloading, and checkpointing. The fgds device > corresponds one-to-one with the PCIe GPU on the machine. The usage is > straightforward: an application first calls ioctl on the corresponding > fgds device with REGISTER_BUF, passing the target GPU memory buffer > address (represented as a dma-buf fd). Subsequently, the application can > perform direct data transfer between a regular file on NVMe and GPU memory > by invoking ioctl on the fgds device with READ/WRITE, passing the file > descriptor and the handle returned by REGISTER_BUF. > > This solution allows both applications and the fgds device to work > seamlessly with GPUs from different vendors without any modifications. > Furthermore, applications no longer need to call vendor-specific > proprietary APIs (such as NVIDIA's cuFile API) or install vendor-specific > kernel modules (such as NVIDIA's nvidia-fs.ko) for different GPU vendors. > We have tested fgds on NVDIA and AMD GPU cards, and theoretically, it also > works on GPUs from other vendors that support exporting GPU memory via > dma-buf. Some performance test results can be found in [1]. > > This implementation offers several advantages: > - Simplicity and Efficiency: It provides a lightweight and intuitive > interface. The single ioctl call mimics the parameter structure of > pread/pwrite, and its return value semantics are fully compatible with > pread/pwrite > - Isolation and Security: The interface is independent and has a minimal > impact scope. This isolation facilitates fine-grained permission control > on the device node, enhancing security management > > Changes since v1: > - Refactor implementation per Christian's feedback, on top of Pavel's > DMA-buf support for io_uring [2] People already replied, but I'm going to elaborate a bit. That series [2] already exposes a uapi via io_uring, it's device agnostic and all that. The framework was made to allow multiple users, perhaps folks have some kernel-internal users in mind, but a completely new read/write uAPI looks unnecessary, especially an ioctl() based one. It sounds like [1] is already using io_uring, so it doesn't sound like a problem that the entire uapi is io_uring based(?). > - Add validation checks in the XFS I/O submission path to ensure that > bios with the REQ_DMABUF flag are only processed via direct I/O, and > prevents the kernel from attempting to fall back to buffered I/O, which > is incompatible with DMA-buf backed GPU memory regions. While currently > supports only XFS, the underlying mechanism is filesystem-agnostic and > can be adapted for other filesystems with similar minor changes xfs support it a it orthogonal to the uapi question, but it should be implemented in xfs and not side stepped. There is an RFC on the mailing list from Anuj and Nitesh that is trying to do that. > - Address the comments from Greg and the Sashiko AI > > [1] https://github.com/Storage-and-OS-for-AI/fgds > [2] https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git/ > tag/?h=for-7.4/io_uring-dmabuf-20261023 > > Mengmeng Zhao (1): > misc: fgds: introduce GPU-File direct I/O via dma-buf > > .../userspace-api/ioctl/ioctl-number.rst | 2 + > drivers/dma-buf/dma-buf-io.c | 3 + > drivers/misc/Kconfig | 16 + > drivers/misc/Makefile | 1 + > drivers/misc/fgds.c | 506 ++++++++++++++++++ > fs/iomap/buffered-io.c | 11 + > fs/iomap/direct-io.c | 23 +- > fs/xfs/xfs_file.c | 4 + > include/linux/iov_iter.h | 4 + > include/uapi/linux/fgds.h | 124 +++++ > lib/iov_iter.c | 1 + > mm/filemap.c | 22 +- > 12 files changed, 713 insertions(+), 4 deletions(-) > create mode 100644 drivers/misc/fgds.c > create mode 100644 include/uapi/linux/fgds.h > -- Pavel Begunkov ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC PATCH v2 0/1] misc: fgds: introduce GPU-File direct I/O via dma-buf 2026-10-27 13:54 ` [RFC PATCH v2 0/1] " Pavel Begunkov @ 2026-10-28 3:01 ` Li Wang 0 siblings, 0 replies; 13+ messages in thread From: Li Wang @ 2026-10-28 3:01 UTC (permalink / raw) To: Pavel Begunkov, Arnd Bergmann, Greg Kroah-Hartman, Jens Axboe, Christian König Cc: Sumit Semwal, linux-media, dri-devel, linaro-mm-sig, io-uring, linux-kernel, Mengmeng Zhao Hi all, On 27/10/2026 21:54, Pavel Begunkov wrote: > Hey Li, > > On 10/27/26 09:08, Li Wang wrote: ... > > xfs support it a it orthogonal to the uapi question, but it should be > implemented in xfs and not side stepped. There is an RFC on the mailing > list from Anuj and Nitesh that is trying to do that. > Thanks for the feedback! As far as we know, Pavel's current work only supports direct data transfer between GPU memory and NVMe raw block devices. As Pavel pointed out, I tended to prefer an implementation that was as non-intrusive to the filesystem as possible. My rationale was that GPU-NVME direct data transfer would ideally not require filesystem opt-in, since there was no readahead, no caching, and no complex metadata operations involved here. In most cases, the filesystem merely helps translate file offsets into disk block addresses. Additionally, adopting an ioctl interface had a smaller impact scope and provided a simpler userspace synchronous operation interface. We will track the community progress, and we appreciate being let know if there is anything we can help contribute to, as we are active users of this feature. Thanks, Li Wang ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-10-28 3:01 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260908131545.105987-1-liwang@kylinos.cn>
[not found] ` <2026090913-landline-encroach-c544@gregkh>
2026-09-09 10:42 ` [RFC PATCH] misc: fgds: enable GPU-NVMe direct I/O via POSIX and io_uring Li Wang
2026-09-09 13:35 ` Greg Kroah-Hartman
2026-09-09 13:45 ` Christian König
2026-09-10 3:57 ` Li Wang
2026-09-10 8:35 ` Christian König
2026-09-12 12:45 ` Pavel Begunkov
2026-10-27 9:08 ` [RFC PATCH v2 0/1] misc: fgds: introduce GPU-File direct I/O via dma-buf Li Wang
2026-10-27 9:08 ` [RFC PATCH v2 1/1] " Li Wang
2026-10-27 13:03 ` Jens Axboe
2026-10-27 13:12 ` Greg Kroah-Hartman
2026-10-27 13:12 ` Christian König
2026-10-27 13:54 ` [RFC PATCH v2 0/1] " Pavel Begunkov
2026-10-28 3:01 ` Li Wang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox