On Tue, May 13, 2025 at 4:31 AM Christian König <christian.koe...@amd.com> wrote: > > On 5/13/25 11:27, wangtao wrote: > > Add DMA_BUF_IOCTL_RW_FILE to save/restore data from/to a dma-buf. > > Similar approach where rejected before in favor of using udmabuf. > > Is there any reason you can't use that approach as well?
I also recently verified that udmabuf + O_DIRECT works with sendfile(), and you can even MADV_COLLAPSE the underlying shmem if you want. > Regards, > Christian. > > > > > Signed-off-by: wangtao <tao.wang...@honor.com> > > --- > > drivers/dma-buf/dma-buf.c | 8 ++++++++ > > include/linux/dma-buf.h | 3 +++ > > include/uapi/linux/dma-buf.h | 29 +++++++++++++++++++++++++++++ > > 3 files changed, 40 insertions(+) > > > > diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c > > index 5baa83b85515..95d8b0158ffd 100644 > > --- a/drivers/dma-buf/dma-buf.c > > +++ b/drivers/dma-buf/dma-buf.c > > @@ -460,6 +460,7 @@ static long dma_buf_ioctl(struct file *file, > > struct dma_buf *dmabuf; > > struct dma_buf_sync sync; > > enum dma_data_direction direction; > > + struct dma_buf_rw_file kfile; > > int ret; > > > > dmabuf = file->private_data; > > @@ -504,6 +505,13 @@ static long dma_buf_ioctl(struct file *file, > > return dma_buf_import_sync_file(dmabuf, (const void __user > > *)arg); > > #endif > > > > + case DMA_BUF_IOCTL_RW_FILE: > > + if (copy_from_user(&kfile, (void __user *) arg, > > sizeof(kfile))) > > + return -EFAULT; > > + if (!dmabuf->ops->rw_file) > > + return -EINVAL; > > + return dmabuf->ops->rw_file(dmabuf, &kfile); > > + > > default: > > return -ENOTTY; > > } > > diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h > > index 36216d28d8bd..de236ba2094b 100644 > > --- a/include/linux/dma-buf.h > > +++ b/include/linux/dma-buf.h > > @@ -22,6 +22,7 @@ > > #include <linux/fs.h> > > #include <linux/dma-fence.h> > > #include <linux/wait.h> > > +#include <uapi/linux/dma-buf.h> > > > > struct device; > > struct dma_buf; > > @@ -285,6 +286,8 @@ struct dma_buf_ops { > > > > int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map); > > void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map); > > + > > + int (*rw_file)(struct dma_buf *dmabuf, struct dma_buf_rw_file *file); > > }; > > > > /** > > diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h > > index 5a6fda66d9ad..ec9164b7b753 100644 > > --- a/include/uapi/linux/dma-buf.h > > +++ b/include/uapi/linux/dma-buf.h > > @@ -167,6 +167,29 @@ struct dma_buf_import_sync_file { > > __s32 fd; > > }; > > > > +/** > > + * struct dma_buf_rw_file - read/write file associated with a dma-buf > > + * > > + * Userspace can performs a DMA_BUF_IOCTL_BACK to save data from a dma-buf > > or > > + * restore data to a dma-buf. > > + */ > > +struct dma_buf_rw_file { > > + > > + /** @flags: Flags indicating read/write for this dma-buf. */ > > + __u32 flags; > > + /** @fd: File descriptor of the file associated with this dma-buf. */ > > + __s32 fd; > > + /** @file_offset: Offset within the file where this dma-buf starts. > > + * > > + * Offset and Length must be page-aligned for direct I/O. > > + */ > > + __u64 file_offset; > > + /** @buf_offset: Offset within this dma-buf where the read/write > > starts. */ > > + __u64 buf_offset; > > + /** @buf_len: Length of this dma-buf read/write. */ > > + __u64 buf_len; > > +}; > > + > > #define DMA_BUF_BASE 'b' > > #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) > > > > @@ -179,4 +202,10 @@ struct dma_buf_import_sync_file { > > #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct > > dma_buf_export_sync_file) > > #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct > > dma_buf_import_sync_file) > > > > +#define DMA_BUF_RW_FLAGS_OP_MASK (0xFF << 0) > > +#define DMA_BUF_RW_FLAGS_READ (1 << 0) /* Restore dma-buf data */ > > +#define DMA_BUF_RW_FLAGS_WRITE (2 << 0) /* Save dma-buf data */ > > +#define DMA_BUF_RW_FLAGS_DIRECT (1u << 31) /* Direct read/write file */ > > +#define DMA_BUF_IOCTL_RW_FILE _IOW(DMA_BUF_BASE, 4, struct > > dma_buf_rw_file) > > + > > #endif >
#include <cstdlib> #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/sendfile.h> #include <unistd.h> #include <linux/udmabuf.h> namespace { int createMemfd(size_t bytes, unsigned int flags) { int fd(memfd_create("memfd_test", flags)); if (fd == -1) { perror("memfd_create"); return -1; } int rc = ftruncate(fd, bytes); if (rc == -1) { perror("ftruncate"); return -1; } return fd; } int createUdmabuf(size_t bytes, int memfd) { int udmabuf_dev_fd(open("/dev/udmabuf", O_RDONLY)); if (udmabuf_dev_fd == -1) { perror("udmabuf open"); return -1; } struct udmabuf_create create; create.memfd = memfd; create.flags = UDMABUF_FLAGS_CLOEXEC; create.offset = 0; create.size = bytes; int dmabuf_fd(ioctl(udmabuf_dev_fd, UDMABUF_CREATE, &create)); if (dmabuf_fd == -1) perror("UDMABUF_CREATE"); return dmabuf_fd; } } // anonymous namespace int main(int argc, char **argv) { size_t bytes = 1ull << 30; int memfd = createMemfd(bytes, MFD_ALLOW_SEALING); if (memfd < 0) return EXIT_FAILURE; if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK) < 0) { perror("F_SEAL_SHRINK"); return EXIT_FAILURE; } if (ftruncate(memfd, bytes)) { perror("ftruncate"); return EXIT_FAILURE; } int bigfile = open(argv[1], O_RDONLY | O_DIRECT); if (bigfile < 0) { perror("open"); return EXIT_FAILURE; } off_t offset = 0; for (ssize_t n = sendfile(memfd, bigfile, &offset, bytes - offset); offset < bytes && n > 0; n = sendfile(memfd, bigfile, &offset, bytes - offset)) {} printf("Offset %ld\n", offset); int udmabuf = createUdmabuf(bytes, memfd); if (udmabuf < 0) return EXIT_FAILURE; return EXIT_SUCCESS; }